From 5ca5ef1afeb458242675d0de1fbee9c5286ea007 Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Tue, 6 Feb 2024 15:22:24 +0100 Subject: [PATCH 01/31] resize images - for fixed layout : smallest ratio such that the image fits in the viewport both horizontally and vertically, and doesn't go out of bounds of the page on the right - for infinite layout : smallest ratio such that the image fits in the viewport both horizontally and vertically --- crates/rnote-engine/src/engine/import.rs | 41 ++++++++++++++++++- .../rnote-engine/src/strokes/bitmapimage.rs | 22 +++++++++- crates/rnote-engine/src/strokes/mod.rs | 2 + crates/rnote-engine/src/strokes/resize.rs | 37 +++++++++++++++++ .../rnote-engine/src/strokes/vectorimage.rs | 21 +++++++++- 5 files changed, 117 insertions(+), 6 deletions(-) create mode 100644 crates/rnote-engine/src/strokes/resize.rs diff --git a/crates/rnote-engine/src/engine/import.rs b/crates/rnote-engine/src/engine/import.rs index 80fb8098b3..6d50d2659e 100644 --- a/crates/rnote-engine/src/engine/import.rs +++ b/crates/rnote-engine/src/engine/import.rs @@ -1,9 +1,11 @@ // Imports use super::{EngineConfig, EngineViewMut, StrokeContent}; +use crate::engine::Layout; use crate::pens::Pen; use crate::pens::PenStyle; use crate::store::chrono_comp::StrokeLayer; use crate::store::StrokeKey; +use crate::strokes::Resize; use crate::strokes::{BitmapImage, Stroke, VectorImage}; use crate::{CloneConfig, Engine, WidgetFlags}; use futures::channel::oneshot; @@ -195,11 +197,24 @@ impl Engine { ) -> oneshot::Receiver> { let (oneshot_sender, oneshot_receiver) = oneshot::channel::>(); + let width_page = self.document.format.width().clone(); + let is_fixed = is_fixed_layout(&self.document.layout); + let point_max: na::OPoint> = self.camera.viewport().maxs; + rayon::spawn(move || { let result = || -> anyhow::Result { let svg_str = String::from_utf8(bytes)?; - VectorImage::from_svg_str(&svg_str, pos, None) + VectorImage::from_svg_str( + &svg_str, + pos, + None, + Some(Resize { + width: width_page, + isfixed_layout: is_fixed, + max_viewpoint: point_max, + }), + ) }; if oneshot_sender.send(result()).is_err() { @@ -222,9 +237,22 @@ impl Engine { ) -> oneshot::Receiver> { let (oneshot_sender, oneshot_receiver) = oneshot::channel::>(); + let width_page = self.document.format.width().clone(); + let is_fixed = is_fixed_layout(&self.document.layout); + let point_max: na::OPoint> = self.camera.viewport().maxs; + rayon::spawn(move || { let result = || -> anyhow::Result { - BitmapImage::from_image_bytes(&bytes, pos, None) + BitmapImage::from_image_bytes( + &bytes, + pos, + None, + Some(Resize { + width: width_page, + isfixed_layout: is_fixed, + max_viewpoint: point_max, + }), + ) }; if oneshot_sender.send(result()).is_err() { @@ -392,3 +420,12 @@ impl Engine { widget_flags } } + +/// checks if the layout is constrained in the horizontal direction +fn is_fixed_layout(layout_type: &Layout) -> bool { + match layout_type { + Layout::FixedSize => true, + Layout::ContinuousVertical => true, + _ => false, + } +} diff --git a/crates/rnote-engine/src/strokes/bitmapimage.rs b/crates/rnote-engine/src/strokes/bitmapimage.rs index deb324a5ed..8f6c2b2e62 100644 --- a/crates/rnote-engine/src/strokes/bitmapimage.rs +++ b/crates/rnote-engine/src/strokes/bitmapimage.rs @@ -1,8 +1,10 @@ // Imports +use super::Resize; use super::{Content, Stroke}; use crate::document::Format; use crate::engine::import::{PdfImportPageSpacing, PdfImportPrefs}; use crate::render; +use crate::strokes::resize::calculate_resize; use crate::Drawable; use anyhow::Context; use kurbo::Shape; @@ -101,10 +103,26 @@ impl BitmapImage { bytes: &[u8], pos: na::Vector2, size: Option>, + resize: Option, ) -> Result { let image = render::Image::try_from_encoded_bytes(bytes)?; + + // ratio to resize the image if needed + let resize_ratio = match resize { + None => 1.0, + Some(resize_struct) => calculate_resize( + resize_struct, + na::Vector2::new(f64::from(image.pixel_width), f64::from(image.pixel_height)), + pos, + ), + }; + tracing::debug!("the resize ratio is {resize_ratio}"); + let size = size.unwrap_or_else(|| { - na::vector![f64::from(image.pixel_width), f64::from(image.pixel_height)] + na::vector![ + f64::from(image.pixel_width) * resize_ratio, + f64::from(image.pixel_height) * resize_ratio + ] }); let rectangle = Rectangle { cuboid: p2d::shape::Cuboid::new(size * 0.5), @@ -207,7 +225,7 @@ impl BitmapImage { .collect::, na::Vector2, na::Vector2)>>>()?; pngs.into_par_iter() - .map(|(png_data, pos, size)| Self::from_image_bytes(&png_data, pos, Some(size))) + .map(|(png_data, pos, size)| Self::from_image_bytes(&png_data, pos, Some(size), None)) .collect() } } diff --git a/crates/rnote-engine/src/strokes/mod.rs b/crates/rnote-engine/src/strokes/mod.rs index d59f82efaa..0e9d8ec393 100644 --- a/crates/rnote-engine/src/strokes/mod.rs +++ b/crates/rnote-engine/src/strokes/mod.rs @@ -2,6 +2,7 @@ pub mod bitmapimage; pub mod brushstroke; pub mod content; +pub mod resize; pub mod shapestroke; pub mod stroke; pub mod textstroke; @@ -11,6 +12,7 @@ pub mod vectorimage; pub use bitmapimage::BitmapImage; pub use brushstroke::BrushStroke; pub use content::Content; +pub use resize::Resize; pub use shapestroke::ShapeStroke; pub use stroke::Stroke; pub use textstroke::TextStroke; diff --git a/crates/rnote-engine/src/strokes/resize.rs b/crates/rnote-engine/src/strokes/resize.rs new file mode 100644 index 0000000000..4bf546a863 --- /dev/null +++ b/crates/rnote-engine/src/strokes/resize.rs @@ -0,0 +1,37 @@ +use na; + +#[derive(Debug)] +pub struct Resize { + // size of a page + pub width: f64, + // if the layout has a fixed size vertically + pub isfixed_layout: bool, + // viewport + pub max_viewpoint: na::OPoint>, +} + +pub fn calculate_resize( + resize: Resize, + initial_size: na::Vector2, + pos: na::Vector2, +) -> f64 { + let current_width = initial_size.x; + let current_height = initial_size.y; + + // calculate the minimum ratio to stay in the viewport + let ratio_viewport = (1.0f64) + .min( + //check in the horizontal direction + (resize.max_viewpoint.x - pos.index(0)) / (current_width), + ) + .min( + //check in the vertical direction + (resize.max_viewpoint.y - pos.index(1)) / (current_height), + ); + + // check if we go out of the viewport in the two directions + match resize.isfixed_layout { + false => ratio_viewport, + true => ratio_viewport.min((resize.width - pos.index(0)) / (current_width)), + } +} diff --git a/crates/rnote-engine/src/strokes/vectorimage.rs b/crates/rnote-engine/src/strokes/vectorimage.rs index 8839eb33c7..cecc806fbd 100644 --- a/crates/rnote-engine/src/strokes/vectorimage.rs +++ b/crates/rnote-engine/src/strokes/vectorimage.rs @@ -1,10 +1,13 @@ // Imports use super::content::GeneratedContentImages; +use super::Resize; use super::{Content, Stroke}; use crate::document::Format; use crate::engine::import::{PdfImportPageSpacing, PdfImportPrefs}; +use crate::strokes::resize::calculate_resize; use crate::{render, Drawable}; use kurbo::Shape; +use na; use p2d::bounding_volume::Aabb; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rnote_compose::color; @@ -139,6 +142,7 @@ impl VectorImage { svg_data: &str, pos: na::Vector2, size: Option>, + resize: Option, ) -> Result { const COORDINATES_PREC: u8 = 3; const TRANSFORMS_PREC: u8 = 4; @@ -156,8 +160,20 @@ impl VectorImage { let mut svg_tree = usvg::Tree::from_str(svg_data, &usvg::Options::default())?; svg_tree.convert_text(&render::USVG_FONTDB); - let intrinsic_size = - na::vector![svg_tree.size.width() as f64, svg_tree.size.height() as f64]; + // ratio to resize the image if needed + let resize_ratio = match resize { + None => 1.0, + Some(resize_struct) => calculate_resize( + resize_struct, + na::Vector2::new(svg_tree.size.width() as f64, svg_tree.size.height() as f64), + pos, + ), + }; + + let intrinsic_size = na::vector![ + svg_tree.size.width() as f64 * resize_ratio, + svg_tree.size.height() as f64 * resize_ratio + ]; let svg_data = svg_tree.to_string(&xml_options); let rectangle = if let Some(size) = size { Rectangle { @@ -304,6 +320,7 @@ impl VectorImage { svg.svg_data.as_str(), svg.bounds.mins.coords, Some(svg.bounds.extents()), + None, ) }) .collect() From a68c55a32de62e544438ef8c5c3a906dc28fcfc7 Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Tue, 6 Feb 2024 17:40:47 +0100 Subject: [PATCH 02/31] fix svg and use the transforms instead --- .../rnote-engine/src/strokes/bitmapimage.rs | 10 ++++++--- .../rnote-engine/src/strokes/vectorimage.rs | 22 +++++++++++-------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/crates/rnote-engine/src/strokes/bitmapimage.rs b/crates/rnote-engine/src/strokes/bitmapimage.rs index 8f6c2b2e62..ec141ffbd8 100644 --- a/crates/rnote-engine/src/strokes/bitmapimage.rs +++ b/crates/rnote-engine/src/strokes/bitmapimage.rs @@ -120,13 +120,17 @@ impl BitmapImage { let size = size.unwrap_or_else(|| { na::vector![ - f64::from(image.pixel_width) * resize_ratio, - f64::from(image.pixel_height) * resize_ratio + f64::from(image.pixel_width), + f64::from(image.pixel_height) ] }); + let mut transform = Transform::default(); + transform.append_scale_mut(na::Vector2::new(resize_ratio, resize_ratio)); + transform.append_translation_mut(pos + size * resize_ratio * 0.5); + let rectangle = Rectangle { cuboid: p2d::shape::Cuboid::new(size * 0.5), - transform: Transform::new_w_isometry(na::Isometry2::new(pos + size * 0.5, 0.0)), + transform: transform, }; Ok(Self { image, rectangle }) } diff --git a/crates/rnote-engine/src/strokes/vectorimage.rs b/crates/rnote-engine/src/strokes/vectorimage.rs index cecc806fbd..b0abbfe0a9 100644 --- a/crates/rnote-engine/src/strokes/vectorimage.rs +++ b/crates/rnote-engine/src/strokes/vectorimage.rs @@ -170,23 +170,27 @@ impl VectorImage { ), }; - let intrinsic_size = na::vector![ - svg_tree.size.width() as f64 * resize_ratio, - svg_tree.size.height() as f64 * resize_ratio - ]; + let intrinsic_size = + na::vector![svg_tree.size.width() as f64, svg_tree.size.height() as f64]; let svg_data = svg_tree.to_string(&xml_options); + let rectangle = if let Some(size) = size { + //construct the transform + let mut transform = Transform::default(); + transform.append_scale_mut(na::Vector2::new(resize_ratio, resize_ratio)); + transform.append_translation_mut(pos + size * resize_ratio * 0.5); Rectangle { cuboid: p2d::shape::Cuboid::new(size * 0.5), - transform: Transform::new_w_isometry(na::Isometry2::new(pos + size * 0.5, 0.0)), + transform: transform, } } else { + // construct the geometric transform + let mut transform = Transform::default(); + transform.append_scale_mut(na::Vector2::new(resize_ratio, resize_ratio)); + transform.append_translation_mut(pos + intrinsic_size * resize_ratio * 0.5); Rectangle { cuboid: p2d::shape::Cuboid::new(intrinsic_size * 0.5), - transform: Transform::new_w_isometry(na::Isometry2::new( - pos + intrinsic_size * 0.5, - 0.0, - )), + transform: transform, } }; From 733d4942ce03164d316a593964cce7cb5f14d892 Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Tue, 6 Feb 2024 17:45:15 +0100 Subject: [PATCH 03/31] fmt --- crates/rnote-engine/src/strokes/bitmapimage.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/rnote-engine/src/strokes/bitmapimage.rs b/crates/rnote-engine/src/strokes/bitmapimage.rs index ec141ffbd8..705c02fb25 100644 --- a/crates/rnote-engine/src/strokes/bitmapimage.rs +++ b/crates/rnote-engine/src/strokes/bitmapimage.rs @@ -119,10 +119,7 @@ impl BitmapImage { tracing::debug!("the resize ratio is {resize_ratio}"); let size = size.unwrap_or_else(|| { - na::vector![ - f64::from(image.pixel_width), - f64::from(image.pixel_height) - ] + na::vector![f64::from(image.pixel_width), f64::from(image.pixel_height)] }); let mut transform = Transform::default(); transform.append_scale_mut(na::Vector2::new(resize_ratio, resize_ratio)); From d5c04a6c05e4537c2e4fe010f21fd58c83752a56 Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Tue, 6 Feb 2024 21:36:05 +0100 Subject: [PATCH 04/31] `is_fixed_layout` as a `Layout` method --- crates/rnote-engine/src/document/mod.rs | 11 +++++++++++ crates/rnote-engine/src/engine/import.rs | 13 ++----------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/crates/rnote-engine/src/document/mod.rs b/crates/rnote-engine/src/document/mod.rs index e1cb90c0d5..35632c054c 100644 --- a/crates/rnote-engine/src/document/mod.rs +++ b/crates/rnote-engine/src/document/mod.rs @@ -80,6 +80,17 @@ impl std::string::ToString for Layout { } } +impl Layout { + /// checks if the layout is constrained in the horizontal direction + pub fn is_fixed_layout(&self) -> bool { + match self { + Layout::FixedSize => true, + Layout::ContinuousVertical => true, + _ => false, + } + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(default, rename = "document")] pub struct Document { diff --git a/crates/rnote-engine/src/engine/import.rs b/crates/rnote-engine/src/engine/import.rs index 6d50d2659e..72ad2b605d 100644 --- a/crates/rnote-engine/src/engine/import.rs +++ b/crates/rnote-engine/src/engine/import.rs @@ -198,7 +198,7 @@ impl Engine { let (oneshot_sender, oneshot_receiver) = oneshot::channel::>(); let width_page = self.document.format.width().clone(); - let is_fixed = is_fixed_layout(&self.document.layout); + let is_fixed = self.document.layout.is_fixed_layout(); let point_max: na::OPoint> = self.camera.viewport().maxs; rayon::spawn(move || { @@ -238,7 +238,7 @@ impl Engine { let (oneshot_sender, oneshot_receiver) = oneshot::channel::>(); let width_page = self.document.format.width().clone(); - let is_fixed = is_fixed_layout(&self.document.layout); + let is_fixed = self.document.layout.is_fixed_layout(); let point_max: na::OPoint> = self.camera.viewport().maxs; rayon::spawn(move || { @@ -420,12 +420,3 @@ impl Engine { widget_flags } } - -/// checks if the layout is constrained in the horizontal direction -fn is_fixed_layout(layout_type: &Layout) -> bool { - match layout_type { - Layout::FixedSize => true, - Layout::ContinuousVertical => true, - _ => false, - } -} From 57a49713d4f4032404cc143c3c03a193fbd9cae3 Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Tue, 6 Feb 2024 21:36:56 +0100 Subject: [PATCH 05/31] remove unused `Layout` crate --- crates/rnote-engine/src/engine/import.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/rnote-engine/src/engine/import.rs b/crates/rnote-engine/src/engine/import.rs index 72ad2b605d..d9cf4524e0 100644 --- a/crates/rnote-engine/src/engine/import.rs +++ b/crates/rnote-engine/src/engine/import.rs @@ -1,6 +1,5 @@ // Imports use super::{EngineConfig, EngineViewMut, StrokeContent}; -use crate::engine::Layout; use crate::pens::Pen; use crate::pens::PenStyle; use crate::store::chrono_comp::StrokeLayer; From 90db401424ca1427c96f43dfd8e64d473f5821b0 Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Wed, 7 Feb 2024 16:23:03 +0100 Subject: [PATCH 06/31] remove `resize` and uses an enum `ImageSizeOption` instead - some light renaming - uses pattern matching in the end functions for more compact code/readable branching --- crates/rnote-engine/src/engine/import.rs | 16 +++-- .../rnote-engine/src/strokes/bitmapimage.rs | 32 ++++----- crates/rnote-engine/src/strokes/resize.rs | 36 ++++++---- .../rnote-engine/src/strokes/vectorimage.rs | 65 +++++++++---------- 4 files changed, 84 insertions(+), 65 deletions(-) diff --git a/crates/rnote-engine/src/engine/import.rs b/crates/rnote-engine/src/engine/import.rs index d9cf4524e0..94bbb1046c 100644 --- a/crates/rnote-engine/src/engine/import.rs +++ b/crates/rnote-engine/src/engine/import.rs @@ -4,6 +4,7 @@ use crate::pens::Pen; use crate::pens::PenStyle; use crate::store::chrono_comp::StrokeLayer; use crate::store::StrokeKey; +use crate::strokes::resize::ImageSizeOption; use crate::strokes::Resize; use crate::strokes::{BitmapImage, Stroke, VectorImage}; use crate::{CloneConfig, Engine, WidgetFlags}; @@ -200,6 +201,14 @@ impl Engine { let is_fixed = self.document.layout.is_fixed_layout(); let point_max: na::OPoint> = self.camera.viewport().maxs; + // check if .size/.total_zoom are better than the current .maxs + tracing::debug!( + "max : {:?} \n zoom {:?}\n size {:?}", + self.camera.viewport().maxs, + self.camera.total_zoom(), + self.camera.size(), + ); + rayon::spawn(move || { let result = || -> anyhow::Result { let svg_str = String::from_utf8(bytes)?; @@ -207,8 +216,7 @@ impl Engine { VectorImage::from_svg_str( &svg_str, pos, - None, - Some(Resize { + ImageSizeOption::ResizeImage(Resize { width: width_page, isfixed_layout: is_fixed, max_viewpoint: point_max, @@ -236,6 +244,7 @@ impl Engine { ) -> oneshot::Receiver> { let (oneshot_sender, oneshot_receiver) = oneshot::channel::>(); + // we get these parameters to enable proper resizing of the windowsœ let width_page = self.document.format.width().clone(); let is_fixed = self.document.layout.is_fixed_layout(); let point_max: na::OPoint> = self.camera.viewport().maxs; @@ -245,8 +254,7 @@ impl Engine { BitmapImage::from_image_bytes( &bytes, pos, - None, - Some(Resize { + ImageSizeOption::ResizeImage(Resize { width: width_page, isfixed_layout: is_fixed, max_viewpoint: point_max, diff --git a/crates/rnote-engine/src/strokes/bitmapimage.rs b/crates/rnote-engine/src/strokes/bitmapimage.rs index 705c02fb25..180707b275 100644 --- a/crates/rnote-engine/src/strokes/bitmapimage.rs +++ b/crates/rnote-engine/src/strokes/bitmapimage.rs @@ -1,10 +1,10 @@ // Imports -use super::Resize; +use super::resize::ImageSizeOption; use super::{Content, Stroke}; use crate::document::Format; use crate::engine::import::{PdfImportPageSpacing, PdfImportPrefs}; use crate::render; -use crate::strokes::resize::calculate_resize; +use crate::strokes::resize::calculate_resize_ratio; use crate::Drawable; use anyhow::Context; use kurbo::Shape; @@ -102,29 +102,27 @@ impl BitmapImage { pub fn from_image_bytes( bytes: &[u8], pos: na::Vector2, - size: Option>, - resize: Option, + size: ImageSizeOption, ) -> Result { let image = render::Image::try_from_encoded_bytes(bytes)?; - // ratio to resize the image if needed - let resize_ratio = match resize { - None => 1.0, - Some(resize_struct) => calculate_resize( - resize_struct, - na::Vector2::new(f64::from(image.pixel_width), f64::from(image.pixel_height)), - pos, + let initial_size = na::vector![f64::from(image.pixel_width), f64::from(image.pixel_height)]; + + // pattern match on ImageSizeOption + let (size, resize_ratio) = match size { + ImageSizeOption::RespectOriginalSize => (initial_size, 1.0f64), + ImageSizeOption::ImposeSize(given_size) => (given_size, 1.0f64), + ImageSizeOption::ResizeImage(resize_struct) => ( + initial_size, + calculate_resize_ratio(resize_struct, initial_size, pos), ), }; tracing::debug!("the resize ratio is {resize_ratio}"); - let size = size.unwrap_or_else(|| { - na::vector![f64::from(image.pixel_width), f64::from(image.pixel_height)] - }); + // general transform let mut transform = Transform::default(); transform.append_scale_mut(na::Vector2::new(resize_ratio, resize_ratio)); transform.append_translation_mut(pos + size * resize_ratio * 0.5); - let rectangle = Rectangle { cuboid: p2d::shape::Cuboid::new(size * 0.5), transform: transform, @@ -226,7 +224,9 @@ impl BitmapImage { .collect::, na::Vector2, na::Vector2)>>>()?; pngs.into_par_iter() - .map(|(png_data, pos, size)| Self::from_image_bytes(&png_data, pos, Some(size), None)) + .map(|(png_data, pos, size)| { + Self::from_image_bytes(&png_data, pos, ImageSizeOption::ImposeSize(size)) + }) .collect() } } diff --git a/crates/rnote-engine/src/strokes/resize.rs b/crates/rnote-engine/src/strokes/resize.rs index 4bf546a863..27e0da8ee8 100644 --- a/crates/rnote-engine/src/strokes/resize.rs +++ b/crates/rnote-engine/src/strokes/resize.rs @@ -1,37 +1,51 @@ use na; +/// Enum that lists the different options for sizing the image +/// +/// Either respect the original image size (in pixel or dimensions) +/// for svg, impose a size, or resize based on the viewport/page +#[derive(Debug)] +pub enum ImageSizeOption { + /// respect the size of the original image (no resizing applied) + RespectOriginalSize, + /// Use the given size + ImposeSize(na::Vector2), + /// Resize the image to canvas/page view + ResizeImage(Resize), +} + #[derive(Debug)] pub struct Resize { - // size of a page + /// width of a page pub width: f64, - // if the layout has a fixed size vertically + /// if the layout has a fixed size vertically pub isfixed_layout: bool, - // viewport + /// viewport pub max_viewpoint: na::OPoint>, } -pub fn calculate_resize( +pub fn calculate_resize_ratio( resize: Resize, - initial_size: na::Vector2, - pos: na::Vector2, + initial_size_image: na::Vector2, + pos_left_top_canvas: na::Vector2, ) -> f64 { - let current_width = initial_size.x; - let current_height = initial_size.y; + let current_width = initial_size_image.x; + let current_height = initial_size_image.y; // calculate the minimum ratio to stay in the viewport let ratio_viewport = (1.0f64) .min( //check in the horizontal direction - (resize.max_viewpoint.x - pos.index(0)) / (current_width), + (resize.max_viewpoint.x - pos_left_top_canvas.index(0)) / (current_width), ) .min( //check in the vertical direction - (resize.max_viewpoint.y - pos.index(1)) / (current_height), + (resize.max_viewpoint.y - pos_left_top_canvas.index(1)) / (current_height), ); // check if we go out of the viewport in the two directions match resize.isfixed_layout { false => ratio_viewport, - true => ratio_viewport.min((resize.width - pos.index(0)) / (current_width)), + true => ratio_viewport.min((resize.width - pos_left_top_canvas.index(0)) / (current_width)), } } diff --git a/crates/rnote-engine/src/strokes/vectorimage.rs b/crates/rnote-engine/src/strokes/vectorimage.rs index b0abbfe0a9..d08cffb640 100644 --- a/crates/rnote-engine/src/strokes/vectorimage.rs +++ b/crates/rnote-engine/src/strokes/vectorimage.rs @@ -1,10 +1,10 @@ // Imports use super::content::GeneratedContentImages; -use super::Resize; +use super::resize::ImageSizeOption; use super::{Content, Stroke}; use crate::document::Format; use crate::engine::import::{PdfImportPageSpacing, PdfImportPrefs}; -use crate::strokes::resize::calculate_resize; +use crate::strokes::resize::calculate_resize_ratio; use crate::{render, Drawable}; use kurbo::Shape; use na; @@ -141,8 +141,7 @@ impl VectorImage { pub fn from_svg_str( svg_data: &str, pos: na::Vector2, - size: Option>, - resize: Option, + size: ImageSizeOption, ) -> Result { const COORDINATES_PREC: u8 = 3; const TRANSFORMS_PREC: u8 = 4; @@ -160,37 +159,36 @@ impl VectorImage { let mut svg_tree = usvg::Tree::from_str(svg_data, &usvg::Options::default())?; svg_tree.convert_text(&render::USVG_FONTDB); - // ratio to resize the image if needed - let resize_ratio = match resize { - None => 1.0, - Some(resize_struct) => calculate_resize( - resize_struct, - na::Vector2::new(svg_tree.size.width() as f64, svg_tree.size.height() as f64), - pos, - ), - }; - + let svg_data: String = svg_tree.to_string(&xml_options); let intrinsic_size = na::vector![svg_tree.size.width() as f64, svg_tree.size.height() as f64]; - let svg_data = svg_tree.to_string(&xml_options); - - let rectangle = if let Some(size) = size { - //construct the transform - let mut transform = Transform::default(); - transform.append_scale_mut(na::Vector2::new(resize_ratio, resize_ratio)); - transform.append_translation_mut(pos + size * resize_ratio * 0.5); - Rectangle { - cuboid: p2d::shape::Cuboid::new(size * 0.5), - transform: transform, + let mut transform = Transform::default(); + + let rectangle = match size { + ImageSizeOption::RespectOriginalSize => { + // Size not given : use the intrisic size + transform.append_translation_mut(pos + intrinsic_size * 0.5); + Rectangle { + cuboid: p2d::shape::Cuboid::new(intrinsic_size * 0.5), + transform: transform, + } } - } else { - // construct the geometric transform - let mut transform = Transform::default(); - transform.append_scale_mut(na::Vector2::new(resize_ratio, resize_ratio)); - transform.append_translation_mut(pos + intrinsic_size * resize_ratio * 0.5); - Rectangle { - cuboid: p2d::shape::Cuboid::new(intrinsic_size * 0.5), - transform: transform, + ImageSizeOption::ImposeSize(given_size) => { + // Size given : use the given size + transform.append_translation_mut(pos + given_size * 0.5); + Rectangle { + cuboid: p2d::shape::Cuboid::new(given_size * 0.5), + transform: transform, + } + } + ImageSizeOption::ResizeImage(resize_struct) => { + // Resize : calculate the ratio + let resize_ratio = calculate_resize_ratio(resize_struct, intrinsic_size, pos); + transform.append_translation_mut(pos + intrinsic_size * resize_ratio * 0.5); + Rectangle { + cuboid: p2d::shape::Cuboid::new(intrinsic_size * resize_ratio * 0.5), + transform: transform, + } } }; @@ -323,8 +321,7 @@ impl VectorImage { Self::from_svg_str( svg.svg_data.as_str(), svg.bounds.mins.coords, - Some(svg.bounds.extents()), - None, + ImageSizeOption::ImposeSize(svg.bounds.extents()), ) }) .collect() From 46bbc243eda7bf0a49fc38d6cfaee7ca529f2c0b Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Wed, 7 Feb 2024 18:01:11 +0100 Subject: [PATCH 07/31] remove tracing debug and more docs --- crates/rnote-engine/src/engine/import.rs | 8 -------- crates/rnote-engine/src/strokes/resize.rs | 12 ++++++++++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/rnote-engine/src/engine/import.rs b/crates/rnote-engine/src/engine/import.rs index 94bbb1046c..5417905e05 100644 --- a/crates/rnote-engine/src/engine/import.rs +++ b/crates/rnote-engine/src/engine/import.rs @@ -201,14 +201,6 @@ impl Engine { let is_fixed = self.document.layout.is_fixed_layout(); let point_max: na::OPoint> = self.camera.viewport().maxs; - // check if .size/.total_zoom are better than the current .maxs - tracing::debug!( - "max : {:?} \n zoom {:?}\n size {:?}", - self.camera.viewport().maxs, - self.camera.total_zoom(), - self.camera.size(), - ); - rayon::spawn(move || { let result = || -> anyhow::Result { let svg_str = String::from_utf8(bytes)?; diff --git a/crates/rnote-engine/src/strokes/resize.rs b/crates/rnote-engine/src/strokes/resize.rs index 27e0da8ee8..6462fbfaa8 100644 --- a/crates/rnote-engine/src/strokes/resize.rs +++ b/crates/rnote-engine/src/strokes/resize.rs @@ -24,6 +24,13 @@ pub struct Resize { pub max_viewpoint: na::OPoint>, } +/// Calculate the `ratio` by which to resize the image such that +/// - it stays fully in view +/// - it does not goes over a page border when the mode has a fixed +/// width size +/// +/// `pos_left_top_canvas` is the position of the top-left corner of +/// the image in documents coordinates pub fn calculate_resize_ratio( resize: Resize, initial_size_image: na::Vector2, @@ -32,7 +39,7 @@ pub fn calculate_resize_ratio( let current_width = initial_size_image.x; let current_height = initial_size_image.y; - // calculate the minimum ratio to stay in the viewport + // calculate the minimum ratio to stay in view let ratio_viewport = (1.0f64) .min( //check in the horizontal direction @@ -43,7 +50,8 @@ pub fn calculate_resize_ratio( (resize.max_viewpoint.y - pos_left_top_canvas.index(1)) / (current_height), ); - // check if we go out of the viewport in the two directions + // check if we go out of the page on the right + // we don't want to go out of the page match resize.isfixed_layout { false => ratio_viewport, true => ratio_viewport.min((resize.width - pos_left_top_canvas.index(0)) / (current_width)), From ebb7a25937bdb77b6aeed6a8f96e1d088a0dab2a Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Wed, 7 Feb 2024 18:06:35 +0100 Subject: [PATCH 08/31] fmt --- crates/rnote-engine/src/strokes/resize.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/rnote-engine/src/strokes/resize.rs b/crates/rnote-engine/src/strokes/resize.rs index 6462fbfaa8..c11a51a185 100644 --- a/crates/rnote-engine/src/strokes/resize.rs +++ b/crates/rnote-engine/src/strokes/resize.rs @@ -26,9 +26,9 @@ pub struct Resize { /// Calculate the `ratio` by which to resize the image such that /// - it stays fully in view -/// - it does not goes over a page border when the mode has a fixed +/// - it does not goes over a page border when the mode has a fixed /// width size -/// +/// /// `pos_left_top_canvas` is the position of the top-left corner of /// the image in documents coordinates pub fn calculate_resize_ratio( From 4f3f8f5a3bf8012652b6cc25d9722a212fab580e Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Sat, 17 Feb 2024 21:28:43 +0100 Subject: [PATCH 09/31] fmt --- crates/rnote-engine/src/strokes/resize.rs | 2 +- crates/rnote-engine/src/strokes/vectorimage.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/rnote-engine/src/strokes/resize.rs b/crates/rnote-engine/src/strokes/resize.rs index c11a51a185..6ef0e9d2fc 100644 --- a/crates/rnote-engine/src/strokes/resize.rs +++ b/crates/rnote-engine/src/strokes/resize.rs @@ -51,7 +51,7 @@ pub fn calculate_resize_ratio( ); // check if we go out of the page on the right - // we don't want to go out of the page + // we don't want to go out of the page for fixed layouts match resize.isfixed_layout { false => ratio_viewport, true => ratio_viewport.min((resize.width - pos_left_top_canvas.index(0)) / (current_width)), diff --git a/crates/rnote-engine/src/strokes/vectorimage.rs b/crates/rnote-engine/src/strokes/vectorimage.rs index 850914f559..04df845b38 100644 --- a/crates/rnote-engine/src/strokes/vectorimage.rs +++ b/crates/rnote-engine/src/strokes/vectorimage.rs @@ -162,7 +162,7 @@ impl VectorImage { svg_tree.size().height() as f64 ]; let svg_data = svg_tree.to_string(&xml_options); - + let mut transform = Transform::default(); let rectangle = match size { ImageSizeOption::RespectOriginalSize => { From 58d2cce5403545a0240c8fb8d01ad324d8de578a Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Sun, 18 Feb 2024 03:06:01 +0100 Subject: [PATCH 10/31] refactor the resize code and add a special paste method to respect borders - slight changes for `resize` calculations - paste whilst respecting borders option (shift + ctrl + v) propagated through a `respect_border` argument and changes to the clipboard to accommodate it --- crates/rnote-engine/src/engine/import.rs | 8 + crates/rnote-engine/src/strokes/resize.rs | 64 +++- crates/rnote-ui/data/ui/appmenu.ui | 4 + crates/rnote-ui/src/appwindow/actions.rs | 372 ++++++++++++---------- crates/rnote-ui/src/appwindow/mod.rs | 4 +- crates/rnote-ui/src/canvas/imexport.rs | 14 +- 6 files changed, 269 insertions(+), 197 deletions(-) diff --git a/crates/rnote-engine/src/engine/import.rs b/crates/rnote-engine/src/engine/import.rs index 5417905e05..2c77bc8c90 100644 --- a/crates/rnote-engine/src/engine/import.rs +++ b/crates/rnote-engine/src/engine/import.rs @@ -194,10 +194,12 @@ impl Engine { &self, pos: na::Vector2, bytes: Vec, + respect_borders: bool, ) -> oneshot::Receiver> { let (oneshot_sender, oneshot_receiver) = oneshot::channel::>(); let width_page = self.document.format.width().clone(); + let height_page = self.document.format.height().clone(); let is_fixed = self.document.layout.is_fixed_layout(); let point_max: na::OPoint> = self.camera.viewport().maxs; @@ -210,8 +212,10 @@ impl Engine { pos, ImageSizeOption::ResizeImage(Resize { width: width_page, + height: height_page, isfixed_layout: is_fixed, max_viewpoint: point_max, + respect_borders: respect_borders, }), ) }; @@ -233,11 +237,13 @@ impl Engine { &self, pos: na::Vector2, bytes: Vec, + respect_borders: bool, ) -> oneshot::Receiver> { let (oneshot_sender, oneshot_receiver) = oneshot::channel::>(); // we get these parameters to enable proper resizing of the windowsœ let width_page = self.document.format.width().clone(); + let height_page = self.document.format.height().clone(); let is_fixed = self.document.layout.is_fixed_layout(); let point_max: na::OPoint> = self.camera.viewport().maxs; @@ -248,8 +254,10 @@ impl Engine { pos, ImageSizeOption::ResizeImage(Resize { width: width_page, + height: height_page, isfixed_layout: is_fixed, max_viewpoint: point_max, + respect_borders, }), ) }; diff --git a/crates/rnote-engine/src/strokes/resize.rs b/crates/rnote-engine/src/strokes/resize.rs index 6ef0e9d2fc..13b673687b 100644 --- a/crates/rnote-engine/src/strokes/resize.rs +++ b/crates/rnote-engine/src/strokes/resize.rs @@ -1,4 +1,5 @@ use na; +use p2d::query::gjk::eps_tol; /// Enum that lists the different options for sizing the image /// @@ -18,10 +19,16 @@ pub enum ImageSizeOption { pub struct Resize { /// width of a page pub width: f64, + /// height of a page + pub height: f64, /// if the layout has a fixed size vertically pub isfixed_layout: bool, /// viewport pub max_viewpoint: na::OPoint>, + /// To force elements to not go over borders + /// maybe enabling that to be on only when borders are active + /// would be a better idea + pub respect_borders: bool, } /// Calculate the `ratio` by which to resize the image such that @@ -29,6 +36,10 @@ pub struct Resize { /// - it does not goes over a page border when the mode has a fixed /// width size /// +/// There is an additional constraint when the `respect_border` +/// bool of the `Resize` struct is true. In this case we disallow +/// images to go over to the next page on the right +/// /// `pos_left_top_canvas` is the position of the top-left corner of /// the image in documents coordinates pub fn calculate_resize_ratio( @@ -39,21 +50,42 @@ pub fn calculate_resize_ratio( let current_width = initial_size_image.x; let current_height = initial_size_image.y; - // calculate the minimum ratio to stay in view - let ratio_viewport = (1.0f64) - .min( - //check in the horizontal direction - (resize.max_viewpoint.x - pos_left_top_canvas.index(0)) / (current_width), - ) - .min( - //check in the vertical direction - (resize.max_viewpoint.y - pos_left_top_canvas.index(1)) / (current_height), - ); - - // check if we go out of the page on the right - // we don't want to go out of the page for fixed layouts - match resize.isfixed_layout { - false => ratio_viewport, - true => ratio_viewport.min((resize.width - pos_left_top_canvas.index(0)) / (current_width)), + let mut ratio = 1.0f64; //start the ratio at 1 + + // check that we do not go out of the canvas view in the x direction + ratio = ratio.min((resize.max_viewpoint.x - pos_left_top_canvas.index(0)) / (current_width)); + // check that we do not go out of view in the y direction + ratio = ratio.min((resize.max_viewpoint.y - pos_left_top_canvas.index(1)) / (current_height)); + + // check if we go out of the page on the right on fixed layout + if resize.isfixed_layout { + ratio = ratio.min((resize.width - pos_left_top_canvas.index(0)) / (current_width)); } + + // check if we have to respect borders + if resize.respect_borders { + ratio = ratio.min(calculate_resize_ratio_respect_borders( + resize, + initial_size_image, + pos_left_top_canvas, + )); + } + ratio +} + +/// calculate the ratio to not go over borders +pub fn calculate_resize_ratio_respect_borders( + resize: Resize, + initial_size_image: na::Vector2, + pos_left_top_canvas: na::Vector2, +) -> f64 { + // beware : we MIGHT have zero as the top position, so we start at at minimum at eps + let next_page_vertical_border = + (pos_left_top_canvas.index(0).max(eps_tol()) / resize.width).ceil() * resize.width; + let next_page_horizontal_border = + (pos_left_top_canvas.index(1).max(eps_tol()) / resize.height).ceil() * resize.height; + + ((next_page_vertical_border - pos_left_top_canvas.index(0)) / initial_size_image.x) + .min((next_page_horizontal_border - pos_left_top_canvas.index(1)) / initial_size_image.y) + .max(eps_tol()) } diff --git a/crates/rnote-ui/data/ui/appmenu.ui b/crates/rnote-ui/data/ui/appmenu.ui index 1cc6a03ad2..8ffa3ba2b8 100644 --- a/crates/rnote-ui/data/ui/appmenu.ui +++ b/crates/rnote-ui/data/ui/appmenu.ui @@ -104,6 +104,10 @@ _Paste win.clipboard-paste + + _Paste whilst respecting borders + win.clipboard-paste-respect-borders + _Export… diff --git a/crates/rnote-ui/src/appwindow/actions.rs b/crates/rnote-ui/src/appwindow/actions.rs index 7b38ef71de..958f6015b1 100644 --- a/crates/rnote-ui/src/appwindow/actions.rs +++ b/crates/rnote-ui/src/appwindow/actions.rs @@ -144,6 +144,9 @@ impl RnAppWindow { self.add_action(&action_clipboard_cut); let action_clipboard_paste = gio::SimpleAction::new("clipboard-paste", None); self.add_action(&action_clipboard_paste); + let action_clipboard_respect_borders = + gio::SimpleAction::new("clipboard-paste-respect-borders", None); + self.add_action(&action_clipboard_respect_borders); let action_active_tab_move_left = gio::SimpleAction::new("active-tab-move-left", None); self.add_action(&action_active_tab_move_left); let action_active_tab_move_right = gio::SimpleAction::new("active-tab-move-right", None); @@ -723,180 +726,14 @@ impl RnAppWindow { })); // Clipboard paste + // the logic has been moved to paste_content to make it possible to add a special paste method + action_clipboard_respect_borders.connect_activate( + clone!(@weak self as appwindow => move |_, _| { + paste_content(appwindow,true); + }), + ); action_clipboard_paste.connect_activate(clone!(@weak self as appwindow => move |_, _| { - let canvas = appwindow.active_tab_wrapper().canvas(); - let content_formats = appwindow.clipboard().formats(); - - // Order matters here, we want to go from specific -> generic, mostly because `text/plain` is contained in other text based formats - if content_formats.contain_mime_type("text/uri-list") { - glib::spawn_future_local(clone!(@weak appwindow => async move { - tracing::debug!("Recognized clipboard content format: files list"); - - match appwindow.clipboard().read_text_future().await { - Ok(Some(text)) => { - let file_paths = text.lines().filter_map(|line| { - let file_path = if let Ok(path_uri) = url::Url::parse(line) { - path_uri.to_file_path().ok()? - } else { - PathBuf::from(&line) - }; - - if file_path.exists() { - Some(file_path) - } else { - None - } - }).collect::>(); - - for file_path in file_paths { - appwindow.open_file_w_dialogs(gio::File::for_path(&file_path), None, true).await; - } - } - Ok(None) => {} - Err(e) => { - tracing::error!("Reading clipboard text while pasting clipboard from path failed, Err: {e:?}"); - - } - } - })); - } else if content_formats.contain_mime_type(StrokeContent::MIME_TYPE) { - glib::spawn_future_local(clone!(@weak canvas, @weak appwindow => async move { - tracing::debug!("Recognized clipboard content format: {}", StrokeContent::MIME_TYPE); - - match appwindow.clipboard().read_future(&[StrokeContent::MIME_TYPE], glib::source::Priority::DEFAULT).await { - Ok((input_stream, _)) => { - let mut acc = Vec::new(); - loop { - match input_stream.read_future(vec![0; CLIPBOARD_INPUT_STREAM_BUFSIZE], glib::source::Priority::DEFAULT).await { - Ok((mut bytes, n)) => { - if n == 0 { - break; - } - acc.append(&mut bytes); - } - Err(e) => { - tracing::error!("Failed to read clipboard input stream, Err: {e:?}"); - acc.clear(); - break; - } - } - } - - if !acc.is_empty() { - match crate::utils::str_from_u8_nul_utf8(&acc) { - Ok(json_string) => { - if let Err(e) = canvas.insert_stroke_content(json_string.to_string()).await { - tracing::error!("Failed to insert stroke content while pasting as `{}`, Err: {e:?}", StrokeContent::MIME_TYPE); - } - } - Err(e) => tracing::error!("Failed to read stroke content &str from clipboard data, Err: {e:?}"), - } - } - } - Err(e) => { - tracing::error!( - "Reading clipboard failed while pasting as `{}`, Err: {e:?}", - StrokeContent::MIME_TYPE - ); - } - }; - })); - } else if content_formats.contain_mime_type("image/svg+xml") { - glib::spawn_future_local(clone!(@weak appwindow => async move { - tracing::debug!("Recognized clipboard content: svg image"); - - match appwindow.clipboard().read_future(&["image/svg+xml"], glib::source::Priority::DEFAULT).await { - Ok((input_stream, _)) => { - let mut acc = Vec::new(); - loop { - match input_stream.read_future(vec![0; CLIPBOARD_INPUT_STREAM_BUFSIZE], glib::source::Priority::DEFAULT).await { - Ok((mut bytes, n)) => { - if n == 0 { - break; - } - acc.append(&mut bytes); - } - Err(e) => { - tracing::error!("Failed to read clipboard input stream while pasting as Svg, Err: {e:?}"); - acc.clear(); - break; - } - } - } - - if !acc.is_empty() { - match crate::utils::str_from_u8_nul_utf8(&acc) { - Ok(text) => { - if let Err(e) = canvas.load_in_vectorimage_bytes(text.as_bytes().to_vec(), None).await { - tracing::error!( - "Loading VectorImage bytes failed while pasting as Svg failed, Err: {e:?}" - ); - }; - } - Err(e) => tracing::error!("Failed to get string from clipboard data while pasting as Svg, Err: {e:?}"), - } - } - } - Err(e) => { - tracing::error!("Failed to read clipboard data while pasting as Svg, Err: {e:?}"); - } - }; - })); - } else if content_formats.contain_mime_type("image/png") || - content_formats.contain_mime_type("image/jpeg") || - content_formats.contain_mime_type("image/jpg") || - content_formats.contain_mime_type("image/tiff") || - content_formats.contain_mime_type("image/bmp") { - const MIMES: [&str; 5] = [ - "image/png", - "image/jpeg", - "image/jpg", - "image/tiff", - "image/bmp", - ]; - if let Some(mime_type) = MIMES.into_iter().find(|&mime| content_formats.contain_mime_type(mime)) { - glib::spawn_future_local(clone!(@weak canvas, @weak appwindow => async move { - tracing::debug!("Recognized clipboard content: bitmap image"); - - match appwindow.clipboard().read_texture_future().await { - Ok(Some(texture)) => { - if let Err(e) = canvas.load_in_bitmapimage_bytes(texture.save_to_png_bytes().to_vec(), None).await { - tracing::error!( - "Loading bitmap image bytes failed while pasting clipboard as {mime_type}, Err: {e:?}" - ); - }; - } - Ok(None) => {} - Err(e) => { - tracing::error!( - "Reading clipboard text failed while pasting clipboard as {mime_type}, Err: {e:?}" - ); - } - }; - })); - } - } else if content_formats.contain_mime_type("text/plain") || content_formats.contain_mime_type("text/plain;charset=utf-8"){ - glib::spawn_future_local(clone!(@weak canvas, @weak appwindow => async move { - tracing::debug!("Recognized clipboard content: plain text"); - - match appwindow.clipboard().read_text_future().await { - Ok(Some(text)) => { - if let Err(e) = canvas.load_in_text(text.to_string(), None) { - tracing::error!("Failed to paste clipboard text, Err: {e:?}"); - } - } - Ok(None) => {} - Err(e) => { - tracing::error!( - "Reading clipboard text failed while pasting clipboard as plain text, Err: {e:?}" - ); - - } - } - })); - } else { - tracing::debug!("Failed to paste clipboard, unsupported MIME-type(s): {:?}", content_formats.mime_types()); - } + paste_content(appwindow,false); })); } @@ -925,6 +762,7 @@ impl RnAppWindow { app.set_accels_for_action("win.clipboard-copy", &["c"]); app.set_accels_for_action("win.clipboard-cut", &["x"]); app.set_accels_for_action("win.clipboard-paste", &["v"]); + app.set_accels_for_action("win.clipboard-paste-respect-borders", &["v"]); app.set_accels_for_action("win.pen-style::brush", &["1"]); app.set_accels_for_action("win.pen-style::shaper", &["2"]); app.set_accels_for_action("win.pen-style::typewriter", &["3"]); @@ -938,3 +776,191 @@ impl RnAppWindow { } } } + +/// logic for pasting content from the clipboard +/// the extra argument `respect_borders` is to change the logic +/// when importing images (for now, limited to only that) +pub fn paste_content(appwindow: RnAppWindow, respect_borders: bool) { + let canvas = appwindow.active_tab_wrapper().canvas(); + let content_formats = appwindow.clipboard().formats(); + // Order matters here, we want to go from specific -> generic, mostly because `text/plain` is contained in other text based formats + if content_formats.contain_mime_type("text/uri-list") { + glib::spawn_future_local(clone!(@weak appwindow => async move { + tracing::debug!("Recognized clipboard content format: files list"); + + match appwindow.clipboard().read_text_future().await { + Ok(Some(text)) => { + let file_paths = text.lines().filter_map(|line| { + let file_path = if let Ok(path_uri) = url::Url::parse(line) { + path_uri.to_file_path().ok()? + } else { + PathBuf::from(&line) + }; + + if file_path.exists() { + Some(file_path) + } else { + None + } + }).collect::>(); + + for file_path in file_paths { + appwindow.open_file_w_dialogs(gio::File::for_path(&file_path), None, true).await; + } + } + Ok(None) => {} + Err(e) => { + tracing::error!("Reading clipboard text while pasting clipboard from path failed, Err: {e:?}"); + + } + } + })); + } else if content_formats.contain_mime_type(StrokeContent::MIME_TYPE) { + glib::spawn_future_local(clone!(@weak canvas, @weak appwindow => async move { + tracing::debug!("Recognized clipboard content format: {}", StrokeContent::MIME_TYPE); + + match appwindow.clipboard().read_future(&[StrokeContent::MIME_TYPE], glib::source::Priority::DEFAULT).await { + Ok((input_stream, _)) => { + let mut acc = Vec::new(); + loop { + match input_stream.read_future(vec![0; CLIPBOARD_INPUT_STREAM_BUFSIZE], glib::source::Priority::DEFAULT).await { + Ok((mut bytes, n)) => { + if n == 0 { + break; + } + acc.append(&mut bytes); + } + Err(e) => { + tracing::error!("Failed to read clipboard input stream, Err: {e:?}"); + acc.clear(); + break; + } + } + } + + if !acc.is_empty() { + match crate::utils::str_from_u8_nul_utf8(&acc) { + Ok(json_string) => { + if let Err(e) = canvas.insert_stroke_content(json_string.to_string()).await { + tracing::error!("Failed to insert stroke content while pasting as `{}`, Err: {e:?}", StrokeContent::MIME_TYPE); + } + } + Err(e) => tracing::error!("Failed to read stroke content &str from clipboard data, Err: {e:?}"), + } + } + } + Err(e) => { + tracing::error!( + "Reading clipboard failed while pasting as `{}`, Err: {e:?}", + StrokeContent::MIME_TYPE + ); + } + }; + })); + } else if content_formats.contain_mime_type("image/svg+xml") { + glib::spawn_future_local(clone!(@weak appwindow => async move { + tracing::debug!("Recognized clipboard content: svg image"); + + match appwindow.clipboard().read_future(&["image/svg+xml"], glib::source::Priority::DEFAULT).await { + Ok((input_stream, _)) => { + let mut acc = Vec::new(); + loop { + match input_stream.read_future(vec![0; CLIPBOARD_INPUT_STREAM_BUFSIZE], glib::source::Priority::DEFAULT).await { + Ok((mut bytes, n)) => { + if n == 0 { + break; + } + acc.append(&mut bytes); + } + Err(e) => { + tracing::error!("Failed to read clipboard input stream while pasting as Svg, Err: {e:?}"); + acc.clear(); + break; + } + } + } + + if !acc.is_empty() { + match crate::utils::str_from_u8_nul_utf8(&acc) { + Ok(text) => { + // need to pass the respect_border to `load_in_vectorimage_bytes` + if let Err(e) = canvas.load_in_vectorimage_bytes(text.as_bytes().to_vec(), None, respect_borders).await { + tracing::error!( + "Loading VectorImage bytes failed while pasting as Svg failed, Err: {e:?}" + ); + }; + } + Err(e) => tracing::error!("Failed to get string from clipboard data while pasting as Svg, Err: {e:?}"), + } + } + } + Err(e) => { + tracing::error!("Failed to read clipboard data while pasting as Svg, Err: {e:?}"); + } + }; + })); + } else if content_formats.contain_mime_type("image/png") + || content_formats.contain_mime_type("image/jpeg") + || content_formats.contain_mime_type("image/jpg") + || content_formats.contain_mime_type("image/tiff") + || content_formats.contain_mime_type("image/bmp") + { + const MIMES: [&str; 5] = [ + "image/png", + "image/jpeg", + "image/jpg", + "image/tiff", + "image/bmp", + ]; + if let Some(mime_type) = MIMES + .into_iter() + .find(|&mime| content_formats.contain_mime_type(mime)) + { + glib::spawn_future_local(clone!(@weak canvas, @weak appwindow => async move { + tracing::debug!("Recognized clipboard content: bitmap image"); + + match appwindow.clipboard().read_texture_future().await { + Ok(Some(texture)) => { + if let Err(e) = canvas.load_in_bitmapimage_bytes(texture.save_to_png_bytes().to_vec(), None,respect_borders).await { + tracing::error!( + "Loading bitmap image bytes failed while pasting clipboard as {mime_type}, Err: {e:?}" + ); + }; + } + Ok(None) => {} + Err(e) => { + tracing::error!( + "Reading clipboard text failed while pasting clipboard as {mime_type}, Err: {e:?}" + ); + } + }; + })); + } + } else if content_formats.contain_mime_type("text/plain") + || content_formats.contain_mime_type("text/plain;charset=utf-8") + { + glib::spawn_future_local(clone!(@weak canvas, @weak appwindow => async move { + tracing::debug!("Recognized clipboard content: plain text"); + + match appwindow.clipboard().read_text_future().await { + Ok(Some(text)) => { + if let Err(e) = canvas.load_in_text(text.to_string(), None) { + tracing::error!("Failed to paste clipboard text, Err: {e:?}"); + } + } + Ok(None) => {} + Err(e) => { + tracing::error!( + "Reading clipboard text failed while pasting clipboard as plain text, Err: {e:?}" + ); + + } + } + })); + } else { + tracing::debug!( + "Failed to paste clipboard, unsupported MIME-type(s): {:?}", + content_formats.mime_types() + ); + } +} diff --git a/crates/rnote-ui/src/appwindow/mod.rs b/crates/rnote-ui/src/appwindow/mod.rs index 3ad77c5f32..ad5499d362 100644 --- a/crates/rnote-ui/src/appwindow/mod.rs +++ b/crates/rnote-ui/src/appwindow/mod.rs @@ -511,7 +511,7 @@ impl RnAppWindow { let canvas = self.active_tab_wrapper().canvas(); let (bytes, _) = input_file.load_bytes_future().await?; canvas - .load_in_vectorimage_bytes(bytes.to_vec(), target_pos) + .load_in_vectorimage_bytes(bytes.to_vec(), target_pos, false) .await?; true } @@ -519,7 +519,7 @@ impl RnAppWindow { let canvas = self.active_tab_wrapper().canvas(); let (bytes, _) = input_file.load_bytes_future().await?; canvas - .load_in_bitmapimage_bytes(bytes.to_vec(), target_pos) + .load_in_bitmapimage_bytes(bytes.to_vec(), target_pos, false) .await?; true } diff --git a/crates/rnote-ui/src/canvas/imexport.rs b/crates/rnote-ui/src/canvas/imexport.rs index 1ed986f37e..305edf2f1a 100644 --- a/crates/rnote-ui/src/canvas/imexport.rs +++ b/crates/rnote-ui/src/canvas/imexport.rs @@ -76,14 +76,15 @@ impl RnCanvas { &self, bytes: Vec, target_pos: Option>, + respect_borders: bool, ) -> anyhow::Result<()> { let pos = self.determine_stroke_import_pos(target_pos); // Splitting the import operation into two parts: a receiver that gets awaited with the content, and // the blocking import avoids borrowing the entire engine RefCell while awaiting the content, avoiding panics. - let vectorimage_receiver = self - .engine_mut() - .generate_vectorimage_from_bytes(pos, bytes); + let vectorimage_receiver = + self.engine_mut() + .generate_vectorimage_from_bytes(pos, bytes, respect_borders); let vectorimage = vectorimage_receiver.await??; let widget_flags = self .engine_mut() @@ -100,12 +101,13 @@ impl RnCanvas { &self, bytes: Vec, target_pos: Option>, + respect_borders: bool, ) -> anyhow::Result<()> { let pos = self.determine_stroke_import_pos(target_pos); - let bitmapimage_receiver = self - .engine_mut() - .generate_bitmapimage_from_bytes(pos, bytes); + let bitmapimage_receiver = + self.engine_mut() + .generate_bitmapimage_from_bytes(pos, bytes, respect_borders); let bitmapimage = bitmapimage_receiver.await??; let widget_flags = self .engine_mut() From 17de25bf28e306dc2f10489e73d1bc632d07a7f6 Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Mon, 19 Feb 2024 00:10:57 +0100 Subject: [PATCH 11/31] shift + drag and drop wip - using a global state to capture shift + drag and drop --- crates/rnote-engine/src/engine/import.rs | 3 ++ crates/rnote-engine/src/engine/mod.rs | 30 +++++++++++++++++ crates/rnote-engine/src/engine/rendering.rs | 1 + .../rnote-engine/src/engine/visual_debug.rs | 1 + crates/rnote-engine/src/pens/penholder.rs | 7 ++++ .../src/pens/selector/penevents.rs | 4 +++ crates/rnote-ui/src/app/mod.rs | 4 +-- crates/rnote-ui/src/appwindow/actions.rs | 32 ++++++++++++++++++- crates/rnote-ui/src/appwindow/mod.rs | 8 +++-- crates/rnote-ui/src/canvas/imexport.rs | 2 ++ crates/rnote-ui/src/canvas/input.rs | 24 ++++++++++++-- crates/rnote-ui/src/canvas/mod.rs | 8 +++-- crates/rnote-ui/src/dialogs/import.rs | 4 +-- .../workspacebrowser/filerow/actions/open.rs | 2 +- crates/rnote-ui/src/workspacebrowser/mod.rs | 2 +- 15 files changed, 117 insertions(+), 15 deletions(-) diff --git a/crates/rnote-engine/src/engine/import.rs b/crates/rnote-engine/src/engine/import.rs index 2c77bc8c90..3017b437f6 100644 --- a/crates/rnote-engine/src/engine/import.rs +++ b/crates/rnote-engine/src/engine/import.rs @@ -168,6 +168,7 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, + dnd: &mut false, }); widget_flags |= self.doc_resize_to_fit_content(); widget_flags.redraw = true; @@ -379,6 +380,7 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, + dnd: &mut self.dnd, }, ); } @@ -419,6 +421,7 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, + dnd: &mut self.dnd, }); widget_flags |= self.store.record(Instant::now()); diff --git a/crates/rnote-engine/src/engine/mod.rs b/crates/rnote-engine/src/engine/mod.rs index 9c01e114cd..7eebf0592e 100644 --- a/crates/rnote-engine/src/engine/mod.rs +++ b/crates/rnote-engine/src/engine/mod.rs @@ -45,6 +45,7 @@ pub struct EngineView<'a> { pub store: &'a StrokeStore, pub camera: &'a Camera, pub audioplayer: &'a Option, + pub dnd: &'a bool, } /// A mutable view into the engine, excluding the penholder. @@ -56,6 +57,7 @@ pub struct EngineViewMut<'a> { pub store: &'a mut StrokeStore, pub camera: &'a mut Camera, pub audioplayer: &'a mut Option, + pub dnd: &'a mut bool, } impl<'a> EngineViewMut<'a> { @@ -68,8 +70,14 @@ impl<'a> EngineViewMut<'a> { store: self.store, camera: self.camera, audioplayer: self.audioplayer, + dnd: self.dnd, } } + + // change a value + pub(crate) fn update_dnd<'m>(&'m mut self, new: bool) { + *self.dnd = new + } } #[derive(Debug, Clone)] @@ -174,6 +182,8 @@ pub struct Engine { audioplayer: Option, #[serde(skip)] visual_debug: bool, + #[serde(skip)] + pub dnd: bool, // the task sender. Must not be modified, only cloned. #[serde(skip)] tasks_tx: EngineTaskSender, @@ -203,6 +213,7 @@ impl Default for Engine { audioplayer: None, visual_debug: false, + dnd: false, tasks_tx: EngineTaskSender(tasks_tx), tasks_rx: Some(EngineTaskReceiver(tasks_rx)), background_tile_image: None, @@ -232,6 +243,7 @@ impl Engine { store: &self.store, camera: &self.camera, audioplayer: &self.audioplayer, + dnd: &self.dnd, } } @@ -244,6 +256,7 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, + dnd: &mut self.dnd, } } @@ -474,10 +487,15 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, + dnd: &mut self.dnd, }, ) } + pub fn update_dnd(&mut self, new: bool) { + self.dnd = new + } + /// Handle a pressed shortcut key. pub fn handle_pressed_shortcut_key( &mut self, @@ -494,6 +512,7 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, + dnd: &mut self.dnd, }, ) } @@ -509,6 +528,7 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, + dnd: &mut self.dnd, }, ) } @@ -527,6 +547,7 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, + dnd: &mut self.dnd, }, ) } @@ -542,6 +563,7 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, + dnd: &mut self.dnd, }, ) } @@ -556,6 +578,7 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, + dnd: &mut self.dnd, }) } @@ -755,6 +778,7 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, + dnd: &mut self.dnd, }) } @@ -770,6 +794,7 @@ impl Engine { store: &self.store, camera: &self.camera, audioplayer: &self.audioplayer, + dnd: &self.dnd, }) } @@ -785,6 +810,7 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, + dnd: &mut self.dnd, }) } @@ -894,6 +920,7 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, + dnd: &mut self.dnd, }, ) } @@ -911,6 +938,7 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, + dnd: &mut self.dnd, }) } widget_flags @@ -931,6 +959,7 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, + dnd: &mut self.dnd, }, ) } @@ -949,6 +978,7 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, + dnd: &mut self.dnd, }, ) } diff --git a/crates/rnote-engine/src/engine/rendering.rs b/crates/rnote-engine/src/engine/rendering.rs index aca3094ad3..53399e7391 100644 --- a/crates/rnote-engine/src/engine/rendering.rs +++ b/crates/rnote-engine/src/engine/rendering.rs @@ -148,6 +148,7 @@ impl Engine { store: &self.store, camera: &self.camera, audioplayer: &self.audioplayer, + dnd: &self.dnd, }, )?; diff --git a/crates/rnote-engine/src/engine/visual_debug.rs b/crates/rnote-engine/src/engine/visual_debug.rs index 5b673ff2ee..7eae943d5c 100644 --- a/crates/rnote-engine/src/engine/visual_debug.rs +++ b/crates/rnote-engine/src/engine/visual_debug.rs @@ -235,6 +235,7 @@ pub(crate) fn draw_stroke_debug_to_gtk_snapshot( store: &engine.store, camera: &engine.camera, audioplayer: &engine.audioplayer, + dnd: &engine.dnd, }) { draw_bounds_to_gtk_snapshot(bounds, COLOR_SELECTOR_BOUNDS, snapshot, border_widths); } diff --git a/crates/rnote-engine/src/pens/penholder.rs b/crates/rnote-engine/src/pens/penholder.rs index 5bace7187f..2f75e63e42 100644 --- a/crates/rnote-engine/src/pens/penholder.rs +++ b/crates/rnote-engine/src/pens/penholder.rs @@ -453,6 +453,13 @@ impl PenHolder { EventPropagation::Stop } + KeyboardKey::ShiftLeft | KeyboardKey::ShiftRight => { + // shift is a modifier for drag and drop, so we capture it here + tracing::debug!("captured shift key for drop state"); + engine_view.update_dnd(true); + + EventPropagation::Stop + } _ => EventPropagation::Proceed, }, }; diff --git a/crates/rnote-engine/src/pens/selector/penevents.rs b/crates/rnote-engine/src/pens/selector/penevents.rs index 1c5819a1a7..9ac7bb8ad7 100644 --- a/crates/rnote-engine/src/pens/selector/penevents.rs +++ b/crates/rnote-engine/src/pens/selector/penevents.rs @@ -84,6 +84,7 @@ impl Selector { ) .pop(); + // ALL of the logic here to have the selection and resizing logic if (engine_view.pens_config.selector_config.style == SelectorStyle::Single || modifier_keys.contains(&ModifierKey::KeyboardShift)) && key_to_add @@ -255,6 +256,8 @@ impl Selector { *current_rotation_angle = new_rotation_angle; } } + // here is ALL of the logic for the resize part + // modifier_keys ? ModifyState::Resize { from_corner, start_bounds, @@ -320,6 +323,7 @@ impl Selector { .component_div(&selection_bounds.extents()); // resize strokes + // THIS WILL REDUCE DOWN TO MUCH LESS HERE engine_view .store .scale_strokes_with_pivot(selection, scale, pivot); diff --git a/crates/rnote-ui/src/app/mod.rs b/crates/rnote-ui/src/app/mod.rs index 1f9dc8fcb2..05d5db1d47 100644 --- a/crates/rnote-ui/src/app/mod.rs +++ b/crates/rnote-ui/src/app/mod.rs @@ -75,7 +75,7 @@ mod imp { { if let Some(input_file) = input_file { glib::spawn_future_local(clone!(@weak appwindow => async move { - appwindow.open_file_w_dialogs(input_file, None, true).await; + appwindow.open_file_w_dialogs(input_file, None, true,false).await; })); } } else { @@ -134,7 +134,7 @@ mod imp { // Loading in input file in the first tab, if Some if let Some(input_file) = input_file { glib::spawn_future_local(clone!(@weak appwindow => async move { - appwindow.open_file_w_dialogs(input_file, None, false).await; + appwindow.open_file_w_dialogs(input_file, None, false,false).await; })); } } diff --git a/crates/rnote-ui/src/appwindow/actions.rs b/crates/rnote-ui/src/appwindow/actions.rs index 958f6015b1..3ac62bb525 100644 --- a/crates/rnote-ui/src/appwindow/actions.rs +++ b/crates/rnote-ui/src/appwindow/actions.rs @@ -685,6 +685,27 @@ impl RnAppWindow { return; } }; + // // check the content length here (3) ? + // // stroke, then the xml then the image ? + // let debug_types=content.iter().map(|(_data,mime_type)|-> &str { + // mime_type.as_str() + // }).collect::>(); + // tracing::debug!("{:?}",debug_types); + + + // let mut stringify=content[0..content.len()-1].iter().map(|(data,_)| {String::from_utf8_lossy(data)}); + // // check what is in the stroke data part + // tracing::debug!("stroke data {:?}",stringify.next()); + + // // xml part ? + // tracing::debug!("xml part {:?}",stringify.next()); + + // // jpg part ? + // // tracing::debug!("jpg part {:?}",stringify.next()); + // let image = render::Image::try_from_encoded_bytes(content.iter().map(|(x,_)| {x}).last().unwrap()).unwrap(); + // tracing::debug!("image part rect \t{:?}\n pixel width\t{:?}\n pixel height\t{:?}\n memory format\t{:?}\n",image.rect,image.pixel_width,image.pixel_height,image.memory_format); + + let gdk_content_provider = gdk::ContentProvider::new_union(content.into_iter().map(|(data, mime_type)| { gdk::ContentProvider::for_bytes(mime_type.as_str(), &glib::Bytes::from_owned(data)) }).collect::>().as_slice()); @@ -783,6 +804,9 @@ impl RnAppWindow { pub fn paste_content(appwindow: RnAppWindow, respect_borders: bool) { let canvas = appwindow.active_tab_wrapper().canvas(); let content_formats = appwindow.clipboard().formats(); + + // tracing::debug!("{:?}",content_formats); + // Order matters here, we want to go from specific -> generic, mostly because `text/plain` is contained in other text based formats if content_formats.contain_mime_type("text/uri-list") { glib::spawn_future_local(clone!(@weak appwindow => async move { @@ -805,7 +829,7 @@ pub fn paste_content(appwindow: RnAppWindow, respect_borders: bool) { }).collect::>(); for file_path in file_paths { - appwindow.open_file_w_dialogs(gio::File::for_path(&file_path), None, true).await; + appwindow.open_file_w_dialogs(gio::File::for_path(&file_path), None, true,respect_borders).await; } } Ok(None) => {} @@ -841,6 +865,12 @@ pub fn paste_content(appwindow: RnAppWindow, respect_borders: bool) { if !acc.is_empty() { match crate::utils::str_from_u8_nul_utf8(&acc) { Ok(json_string) => { + // debug the json_string + // can I change the StrokeContent + let final_thing = serde_json::from_str::(&json_string.to_string()).unwrap(); + tracing::debug!("{:?}",final_thing); + // only strokes, bounds and background a priori + if let Err(e) = canvas.insert_stroke_content(json_string.to_string()).await { tracing::error!("Failed to insert stroke content while pasting as `{}`, Err: {e:?}", StrokeContent::MIME_TYPE); } diff --git a/crates/rnote-ui/src/appwindow/mod.rs b/crates/rnote-ui/src/appwindow/mod.rs index ad5499d362..6cfa9519a1 100644 --- a/crates/rnote-ui/src/appwindow/mod.rs +++ b/crates/rnote-ui/src/appwindow/mod.rs @@ -439,10 +439,11 @@ impl RnAppWindow { input_file: gio::File, target_pos: Option>, rnote_file_new_tab: bool, + respect_border: bool, ) { self.overlays().progressbar_start_pulsing(); match self - .try_open_file(input_file, target_pos, rnote_file_new_tab) + .try_open_file(input_file, target_pos, rnote_file_new_tab, respect_border) .await { Ok(true) => { @@ -469,6 +470,7 @@ impl RnAppWindow { input_file: gio::File, target_pos: Option>, rnote_file_new_tab: bool, + respect_border: bool, ) -> anyhow::Result { let file_imported = match FileType::lookup_file_type(&input_file) { FileType::RnoteFile => { @@ -511,7 +513,7 @@ impl RnAppWindow { let canvas = self.active_tab_wrapper().canvas(); let (bytes, _) = input_file.load_bytes_future().await?; canvas - .load_in_vectorimage_bytes(bytes.to_vec(), target_pos, false) + .load_in_vectorimage_bytes(bytes.to_vec(), target_pos, respect_border) .await?; true } @@ -519,7 +521,7 @@ impl RnAppWindow { let canvas = self.active_tab_wrapper().canvas(); let (bytes, _) = input_file.load_bytes_future().await?; canvas - .load_in_bitmapimage_bytes(bytes.to_vec(), target_pos, false) + .load_in_bitmapimage_bytes(bytes.to_vec(), target_pos, respect_border) .await?; true } diff --git a/crates/rnote-ui/src/canvas/imexport.rs b/crates/rnote-ui/src/canvas/imexport.rs index 305edf2f1a..e18a8a4456 100644 --- a/crates/rnote-ui/src/canvas/imexport.rs +++ b/crates/rnote-ui/src/canvas/imexport.rs @@ -173,6 +173,8 @@ impl RnCanvas { } }); let content = oneshot_receiver.await??; + + tracing::debug!("{:?} {:?}", content.bounds(), content.size()); //calculate bounds let widget_flags = self.engine_mut().insert_stroke_content(content, pos); self.emit_handle_widget_flags(widget_flags); diff --git a/crates/rnote-ui/src/canvas/input.rs b/crates/rnote-ui/src/canvas/input.rs index a0c96c9060..27cf444a27 100644 --- a/crates/rnote-ui/src/canvas/input.rs +++ b/crates/rnote-ui/src/canvas/input.rs @@ -20,7 +20,7 @@ pub(crate) fn handle_pointer_controller_event( let mut widget_flags = WidgetFlags::default(); let touch_drawing = canvas.touch_drawing(); let gdk_event_type = event.event_type(); - let gdk_modifiers = event.modifier_state(); + let gdk_modifiers = event.modifier_state(); // interesting for us let _gdk_device = event.device().unwrap(); let backlog_policy = canvas.engine_ref().penholder.backlog_policy(); let is_stylus = event_is_stylus(event); @@ -225,7 +225,7 @@ pub(crate) fn handle_key_controller_key_pressed( gdk_key: gdk::Key, gdk_modifiers: gdk::ModifierType, ) -> glib::Propagation { - tracing::trace!( + tracing::debug!( "canvas event key pressed - gdk_key: {gdk_key:?}, gdk_modifiers: {gdk_modifiers:?}" ); canvas.grab_focus(); @@ -235,6 +235,17 @@ pub(crate) fn handle_key_controller_key_pressed( let modifier_keys = retrieve_modifier_keys(gdk_modifiers); let shortcut_key = retrieve_keyboard_shortcut_key(gdk_key, gdk_modifiers); + // re print all of this information once again + tracing::debug!( + "key\t {:?} + modifier\t {:?} + shortcut\t {:?} + ", + keyboard_key, + modifier_keys, + shortcut_key + ); + let (propagation, widget_flags) = if let Some(shortcut_key) = shortcut_key { canvas .engine_mut() @@ -259,9 +270,15 @@ pub(crate) fn handle_key_controller_key_released( gdk_key: gdk::Key, gdk_modifiers: gdk::ModifierType, ) { - tracing::trace!( + tracing::debug!( "canvas event key released - gdk_key: {gdk_key:?}, gdk_modifiers: {gdk_modifiers:?}" ); + let keyboard_key = retrieve_keyboard_key(gdk_key); + + match keyboard_key { + KeyboardKey::ShiftLeft | KeyboardKey::ShiftRight => _canvas.engine_mut().update_dnd(false), + _ => {} + } } pub(crate) fn handle_imcontext_text_commit(canvas: &RnCanvas, text: &str) { @@ -447,6 +464,7 @@ fn retrieve_pen_mode(event: &gdk::Event) -> Option { } } +/// correspond to KeyboardCtrlSpace pub(crate) fn retrieve_keyboard_shortcut_key( gdk_key: gdk::Key, modifier: gdk::ModifierType, diff --git a/crates/rnote-ui/src/canvas/mod.rs b/crates/rnote-ui/src/canvas/mod.rs index 43eda6c9c4..b955d77db9 100644 --- a/crates/rnote-ui/src/canvas/mod.rs +++ b/crates/rnote-ui/src/canvas/mod.rs @@ -153,7 +153,7 @@ mod imp { drawing_cursor: RefCell::new(drawing_cursor), drawing_cursor_icon_name: RefCell::new(drawing_cursor_icon_name), invisible_cursor: RefCell::new(invisible_cursor), - pointer_controller, + pointer_controller: pointer_controller, key_controller, key_controller_im_context, drop_target, @@ -1119,18 +1119,22 @@ impl RnCanvas { .build(); // Drop Target + // [OUR DRAG AND DROP PART] + // derived from drop_target let appwindow_drop_target = self.imp().drop_target.connect_drop( clone!(@weak self as canvas, @weak appwindow => @default-return false, move |_, value, x, y| { let pos = (canvas.engine_ref().camera.transform().inverse() * na::point![x,y]).coords; let mut accept_drop = false; + let respect_border = canvas.engine_ref().dnd; + if value.is::() { // In some scenarios, get() can fail with `UnexpectedNone` even though is() returned true, e.g. when dealing with trashed files. match value.get::() { Ok(file) => { glib::spawn_future_local(clone!(@weak appwindow => async move { - appwindow.open_file_w_dialogs(file, Some(pos), true).await; + appwindow.open_file_w_dialogs(file, Some(pos), true,respect_border).await; })); accept_drop = true; }, diff --git a/crates/rnote-ui/src/dialogs/import.rs b/crates/rnote-ui/src/dialogs/import.rs index c21e60301b..8a8675cd81 100644 --- a/crates/rnote-ui/src/dialogs/import.rs +++ b/crates/rnote-ui/src/dialogs/import.rs @@ -34,7 +34,7 @@ pub(crate) async fn filedialog_open_doc(appwindow: &RnAppWindow) { match filedialog.open_future(Some(appwindow)).await { Ok(selected_file) => { appwindow - .open_file_w_dialogs(selected_file, None, true) + .open_file_w_dialogs(selected_file, None, true, false) .await; } Err(e) => { @@ -76,7 +76,7 @@ pub(crate) async fn filedialog_import_file(appwindow: &RnAppWindow) { match dialog.open_future(Some(appwindow)).await { Ok(selected_file) => { appwindow - .open_file_w_dialogs(selected_file, None, true) + .open_file_w_dialogs(selected_file, None, true, false) .await; } Err(e) => { diff --git a/crates/rnote-ui/src/workspacebrowser/filerow/actions/open.rs b/crates/rnote-ui/src/workspacebrowser/filerow/actions/open.rs index e716fbdac9..73ba098401 100644 --- a/crates/rnote-ui/src/workspacebrowser/filerow/actions/open.rs +++ b/crates/rnote-ui/src/workspacebrowser/filerow/actions/open.rs @@ -10,7 +10,7 @@ pub(crate) fn open(filerow: &RnFileRow, appwindow: &RnAppWindow) -> gio::SimpleA clone!(@weak filerow, @weak appwindow => move |_action_open_file, _| { if let Some(current_file) = filerow.current_file() { glib::spawn_future_local(clone!(@weak appwindow => async move { - appwindow.open_file_w_dialogs(current_file, None, true).await; + appwindow.open_file_w_dialogs(current_file, None, true,false).await; })); } }), diff --git a/crates/rnote-ui/src/workspacebrowser/mod.rs b/crates/rnote-ui/src/workspacebrowser/mod.rs index 0d47274209..08d9469daf 100644 --- a/crates/rnote-ui/src/workspacebrowser/mod.rs +++ b/crates/rnote-ui/src/workspacebrowser/mod.rs @@ -298,7 +298,7 @@ impl RnWorkspaceBrowser { let file_info = listview.model().unwrap().item(position).unwrap().downcast::().unwrap(); if let Some(input_file) = file_info.attribute_object("standard::file") { glib::spawn_future_local(clone!(@weak appwindow => async move { - appwindow.open_file_w_dialogs(input_file.downcast::().unwrap(), None, true).await; + appwindow.open_file_w_dialogs(input_file.downcast::().unwrap(), None, true,false).await; })); }; folders_filter.changed(FilterChange::Different); From 228b9fef394b1acff1b30499ccb6edd8c977fcc3 Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Mon, 19 Feb 2024 12:35:55 +0100 Subject: [PATCH 12/31] modularize the `resize` file --- .gitignore | 3 +- crates/rnote-engine/src/strokes/resize.rs | 109 ++++++++++++++++------ 2 files changed, 80 insertions(+), 32 deletions(-) diff --git a/.gitignore b/.gitignore index e227fb0425..2c79294853 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ *.code-workspace .idea/ .toggletasks.json -*.flatpak \ No newline at end of file +*.flatpak +IMSIZE.md diff --git a/crates/rnote-engine/src/strokes/resize.rs b/crates/rnote-engine/src/strokes/resize.rs index 13b673687b..e567921e71 100644 --- a/crates/rnote-engine/src/strokes/resize.rs +++ b/crates/rnote-engine/src/strokes/resize.rs @@ -1,5 +1,4 @@ use na; -use p2d::query::gjk::eps_tol; /// Enum that lists the different options for sizing the image /// @@ -31,6 +30,15 @@ pub struct Resize { pub respect_borders: bool, } +/// Helper function to calculate ratios +fn helper_calculate_fit_ratio( + max_position: &f64, + current_position: &f64, + current_size: &f64, +) -> f64 { + (max_position - current_position) / current_size +} + /// Calculate the `ratio` by which to resize the image such that /// - it stays fully in view /// - it does not goes over a page border when the mode has a fixed @@ -50,42 +58,81 @@ pub fn calculate_resize_ratio( let current_width = initial_size_image.x; let current_height = initial_size_image.y; - let mut ratio = 1.0f64; //start the ratio at 1 + // compile all ratio in a vec + let ratios = vec![ + // check that we do not go out of the canvas view in the x direction + helper_calculate_fit_ratio( + &resize.max_viewpoint.x, + &pos_left_top_canvas.index(0), + ¤t_width, + ), + // check that we do not go out of view in the y direction + helper_calculate_fit_ratio( + &resize.max_viewpoint.y, + &pos_left_top_canvas.index(1), + ¤t_height, + ), + // check if we go out of the page on the right on fixed layout + helper_calculate_fit_ratio(&resize.width, &pos_left_top_canvas.index(0), ¤t_width), + // check if we have to respect borders + calculate_resize_ratio_respect_borders(&resize, &initial_size_image, &pos_left_top_canvas), + ]; - // check that we do not go out of the canvas view in the x direction - ratio = ratio.min((resize.max_viewpoint.x - pos_left_top_canvas.index(0)) / (current_width)); - // check that we do not go out of view in the y direction - ratio = ratio.min((resize.max_viewpoint.y - pos_left_top_canvas.index(1)) / (current_height)); + // apply rules + let apply_ratios = vec![ + true, //canvas in the x direction + true, //canvas in the y direction + resize.isfixed_layout, //do not go over the page on the right for fixed layout + resize.respect_borders, //do not go over the page on the right for all layouts (slightly redundant) + ]; - // check if we go out of the page on the right on fixed layout - if resize.isfixed_layout { - ratio = ratio.min((resize.width - pos_left_top_canvas.index(0)) / (current_width)); - } - - // check if we have to respect borders - if resize.respect_borders { - ratio = ratio.min(calculate_resize_ratio_respect_borders( - resize, - initial_size_image, - pos_left_top_canvas, - )); - } - ratio + ratios + .iter() + .zip(apply_ratios) + .filter(|x| x.1) + .fold(1.0f64, |acc, x| acc.min(*x.0)) + .max(1e-8f64) //force the value to be positive as a zero would incurr crashes when applying the transforms } /// calculate the ratio to not go over borders pub fn calculate_resize_ratio_respect_borders( - resize: Resize, - initial_size_image: na::Vector2, - pos_left_top_canvas: na::Vector2, + resize: &Resize, + initial_size_image: &na::Vector2, + pos_left_top_canvas: &na::Vector2, ) -> f64 { - // beware : we MIGHT have zero as the top position, so we start at at minimum at eps - let next_page_vertical_border = - (pos_left_top_canvas.index(0).max(eps_tol()) / resize.width).ceil() * resize.width; - let next_page_horizontal_border = - (pos_left_top_canvas.index(1).max(eps_tol()) / resize.height).ceil() * resize.height; + // closure to calculate the ceil + // We take the `floor (ratio + eps) + 1`` + // This allows for element that would fall exactly + // on a border to be on the next page and disallow + // too small ratios + let f_ratio = |position: &f64, size: &f64| -> f64 { + (((position / size) + 1e-8f64).floor() + 1.0f64) * size + }; + + let next_page_vertical_border = f_ratio(&pos_left_top_canvas.index(0), &resize.width); + let next_page_horizontal_border = f_ratio(&pos_left_top_canvas.index(1), &resize.height); + + let ratios = vec![ + helper_calculate_fit_ratio( + &next_page_vertical_border, + &pos_left_top_canvas.index(0), + &initial_size_image.x, + ), + helper_calculate_fit_ratio( + &next_page_horizontal_border, + &pos_left_top_canvas.index(1), + &initial_size_image.y, + ), + ]; + + let rule_apply = vec![ + true, // vertical rule + true, // horizontal rule + ]; - ((next_page_vertical_border - pos_left_top_canvas.index(0)) / initial_size_image.x) - .min((next_page_horizontal_border - pos_left_top_canvas.index(1)) / initial_size_image.y) - .max(eps_tol()) + ratios + .iter() + .zip(rule_apply) + .fold(1.0f64, |acc, x| acc.min(*x.0)) + .max(1e-8f64) } From 7d8c3e4f72244c74b402f8839481b1102357e239 Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Mon, 19 Feb 2024 22:57:32 +0100 Subject: [PATCH 13/31] use mutable cells to hold information about dnd status and modifiers --- crates/rnote-engine/src/engine/import.rs | 3 - crates/rnote-engine/src/engine/mod.rs | 30 ---- crates/rnote-engine/src/engine/rendering.rs | 1 - .../rnote-engine/src/engine/visual_debug.rs | 1 - crates/rnote-engine/src/pens/penholder.rs | 7 - crates/rnote-engine/src/strokes/resize.rs | 4 +- crates/rnote-ui/src/canvas/input.rs | 150 ++++++++++-------- crates/rnote-ui/src/canvas/mod.rs | 26 ++- 8 files changed, 112 insertions(+), 110 deletions(-) diff --git a/crates/rnote-engine/src/engine/import.rs b/crates/rnote-engine/src/engine/import.rs index 3017b437f6..2c77bc8c90 100644 --- a/crates/rnote-engine/src/engine/import.rs +++ b/crates/rnote-engine/src/engine/import.rs @@ -168,7 +168,6 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, - dnd: &mut false, }); widget_flags |= self.doc_resize_to_fit_content(); widget_flags.redraw = true; @@ -380,7 +379,6 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, - dnd: &mut self.dnd, }, ); } @@ -421,7 +419,6 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, - dnd: &mut self.dnd, }); widget_flags |= self.store.record(Instant::now()); diff --git a/crates/rnote-engine/src/engine/mod.rs b/crates/rnote-engine/src/engine/mod.rs index 7eebf0592e..9c01e114cd 100644 --- a/crates/rnote-engine/src/engine/mod.rs +++ b/crates/rnote-engine/src/engine/mod.rs @@ -45,7 +45,6 @@ pub struct EngineView<'a> { pub store: &'a StrokeStore, pub camera: &'a Camera, pub audioplayer: &'a Option, - pub dnd: &'a bool, } /// A mutable view into the engine, excluding the penholder. @@ -57,7 +56,6 @@ pub struct EngineViewMut<'a> { pub store: &'a mut StrokeStore, pub camera: &'a mut Camera, pub audioplayer: &'a mut Option, - pub dnd: &'a mut bool, } impl<'a> EngineViewMut<'a> { @@ -70,14 +68,8 @@ impl<'a> EngineViewMut<'a> { store: self.store, camera: self.camera, audioplayer: self.audioplayer, - dnd: self.dnd, } } - - // change a value - pub(crate) fn update_dnd<'m>(&'m mut self, new: bool) { - *self.dnd = new - } } #[derive(Debug, Clone)] @@ -182,8 +174,6 @@ pub struct Engine { audioplayer: Option, #[serde(skip)] visual_debug: bool, - #[serde(skip)] - pub dnd: bool, // the task sender. Must not be modified, only cloned. #[serde(skip)] tasks_tx: EngineTaskSender, @@ -213,7 +203,6 @@ impl Default for Engine { audioplayer: None, visual_debug: false, - dnd: false, tasks_tx: EngineTaskSender(tasks_tx), tasks_rx: Some(EngineTaskReceiver(tasks_rx)), background_tile_image: None, @@ -243,7 +232,6 @@ impl Engine { store: &self.store, camera: &self.camera, audioplayer: &self.audioplayer, - dnd: &self.dnd, } } @@ -256,7 +244,6 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, - dnd: &mut self.dnd, } } @@ -487,15 +474,10 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, - dnd: &mut self.dnd, }, ) } - pub fn update_dnd(&mut self, new: bool) { - self.dnd = new - } - /// Handle a pressed shortcut key. pub fn handle_pressed_shortcut_key( &mut self, @@ -512,7 +494,6 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, - dnd: &mut self.dnd, }, ) } @@ -528,7 +509,6 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, - dnd: &mut self.dnd, }, ) } @@ -547,7 +527,6 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, - dnd: &mut self.dnd, }, ) } @@ -563,7 +542,6 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, - dnd: &mut self.dnd, }, ) } @@ -578,7 +556,6 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, - dnd: &mut self.dnd, }) } @@ -778,7 +755,6 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, - dnd: &mut self.dnd, }) } @@ -794,7 +770,6 @@ impl Engine { store: &self.store, camera: &self.camera, audioplayer: &self.audioplayer, - dnd: &self.dnd, }) } @@ -810,7 +785,6 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, - dnd: &mut self.dnd, }) } @@ -920,7 +894,6 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, - dnd: &mut self.dnd, }, ) } @@ -938,7 +911,6 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, - dnd: &mut self.dnd, }) } widget_flags @@ -959,7 +931,6 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, - dnd: &mut self.dnd, }, ) } @@ -978,7 +949,6 @@ impl Engine { store: &mut self.store, camera: &mut self.camera, audioplayer: &mut self.audioplayer, - dnd: &mut self.dnd, }, ) } diff --git a/crates/rnote-engine/src/engine/rendering.rs b/crates/rnote-engine/src/engine/rendering.rs index 53399e7391..aca3094ad3 100644 --- a/crates/rnote-engine/src/engine/rendering.rs +++ b/crates/rnote-engine/src/engine/rendering.rs @@ -148,7 +148,6 @@ impl Engine { store: &self.store, camera: &self.camera, audioplayer: &self.audioplayer, - dnd: &self.dnd, }, )?; diff --git a/crates/rnote-engine/src/engine/visual_debug.rs b/crates/rnote-engine/src/engine/visual_debug.rs index 7eae943d5c..5b673ff2ee 100644 --- a/crates/rnote-engine/src/engine/visual_debug.rs +++ b/crates/rnote-engine/src/engine/visual_debug.rs @@ -235,7 +235,6 @@ pub(crate) fn draw_stroke_debug_to_gtk_snapshot( store: &engine.store, camera: &engine.camera, audioplayer: &engine.audioplayer, - dnd: &engine.dnd, }) { draw_bounds_to_gtk_snapshot(bounds, COLOR_SELECTOR_BOUNDS, snapshot, border_widths); } diff --git a/crates/rnote-engine/src/pens/penholder.rs b/crates/rnote-engine/src/pens/penholder.rs index 2f75e63e42..5bace7187f 100644 --- a/crates/rnote-engine/src/pens/penholder.rs +++ b/crates/rnote-engine/src/pens/penholder.rs @@ -453,13 +453,6 @@ impl PenHolder { EventPropagation::Stop } - KeyboardKey::ShiftLeft | KeyboardKey::ShiftRight => { - // shift is a modifier for drag and drop, so we capture it here - tracing::debug!("captured shift key for drop state"); - engine_view.update_dnd(true); - - EventPropagation::Stop - } _ => EventPropagation::Proceed, }, }; diff --git a/crates/rnote-engine/src/strokes/resize.rs b/crates/rnote-engine/src/strokes/resize.rs index e567921e71..830ee8af28 100644 --- a/crates/rnote-engine/src/strokes/resize.rs +++ b/crates/rnote-engine/src/strokes/resize.rs @@ -91,7 +91,7 @@ pub fn calculate_resize_ratio( .zip(apply_ratios) .filter(|x| x.1) .fold(1.0f64, |acc, x| acc.min(*x.0)) - .max(1e-8f64) //force the value to be positive as a zero would incurr crashes when applying the transforms + .max(1e-15f64) //force the value to be positive as a zero would incurr crashes when applying the transforms } /// calculate the ratio to not go over borders @@ -134,5 +134,5 @@ pub fn calculate_resize_ratio_respect_borders( .iter() .zip(rule_apply) .fold(1.0f64, |acc, x| acc.min(*x.0)) - .max(1e-8f64) + .max(1e-15f64) //force the final value to be positive } diff --git a/crates/rnote-ui/src/canvas/input.rs b/crates/rnote-ui/src/canvas/input.rs index 27cf444a27..8bfb920f9b 100644 --- a/crates/rnote-ui/src/canvas/input.rs +++ b/crates/rnote-ui/src/canvas/input.rs @@ -66,36 +66,42 @@ pub(crate) fn handle_pointer_controller_event( let gdk_button = button_event.button(); let mut handle_shortcut_key = false; - tracing::trace!( + // check this with a stylus + tracing::debug!( "canvas event ButtonPress - gdk_button: {gdk_button}, is_stylus: {is_stylus}" ); - if is_stylus { - if gdk_button == gdk::BUTTON_PRIMARY - || gdk_button == gdk::BUTTON_SECONDARY - || gdk_button == gdk::BUTTON_MIDDLE - { - handle_pen_event = true; - handle_shortcut_key = true; - } + if canvas.imp().dnd_status.get() { + handle_pen_event = false; + canvas.imp().dnd_respect_borders.set(true); } else { - #[allow(clippy::collapsible_else_if)] - if gdk_button == gdk::BUTTON_PRIMARY || gdk_button == gdk::BUTTON_SECONDARY { - handle_pen_event = true; - handle_shortcut_key = true; - pen_state = PenState::Down; + if is_stylus { + if gdk_button == gdk::BUTTON_PRIMARY + || gdk_button == gdk::BUTTON_SECONDARY + || gdk_button == gdk::BUTTON_MIDDLE + { + handle_pen_event = true; + handle_shortcut_key = true; + } + } else { + #[allow(clippy::collapsible_else_if)] + if gdk_button == gdk::BUTTON_PRIMARY || gdk_button == gdk::BUTTON_SECONDARY { + handle_pen_event = true; + handle_shortcut_key = true; + pen_state = PenState::Down; + } } - } - if handle_shortcut_key { - let shortcut_key = retrieve_button_shortcut_key(gdk_button, is_stylus); + if handle_shortcut_key { + let shortcut_key = retrieve_button_shortcut_key(gdk_button, is_stylus); - if let Some(shortcut_key) = shortcut_key { - let (ep, wf) = canvas - .engine_mut() - .handle_pressed_shortcut_key(shortcut_key, now); - widget_flags |= wf; - propagation = ep.into_glib(); + if let Some(shortcut_key) = shortcut_key { + let (ep, wf) = canvas + .engine_mut() + .handle_pressed_shortcut_key(shortcut_key, now); + widget_flags |= wf; + propagation = ep.into_glib(); + } } } } @@ -103,31 +109,37 @@ pub(crate) fn handle_pointer_controller_event( let button_event = event.downcast_ref::().unwrap(); let gdk_button = button_event.button(); - tracing::trace!( + // check this with a stylus + tracing::debug!( "canvas event ButtonRelease - gdk_button: {gdk_button}, is_stylus: {is_stylus}" ); - if is_stylus { - if gdk_button == gdk::BUTTON_PRIMARY - || gdk_button == gdk::BUTTON_SECONDARY - || gdk_button == gdk::BUTTON_MIDDLE - { - handle_pen_event = true; - } - - // again, this is the method to detect proximity on stylus. - if gdk_button == gdk::BUTTON_PRIMARY { - pen_state = PenState::Up; - } else { - pen_state = PenState::Proximity; - } + if canvas.imp().dnd_status.get() { + handle_pen_event = false; + canvas.imp().dnd_respect_borders.set(false); } else { - #[allow(clippy::collapsible_else_if)] - if gdk_button == gdk::BUTTON_PRIMARY || gdk_button == gdk::BUTTON_SECONDARY { - pen_state = PenState::Up; - handle_pen_event = true; - } - }; + if is_stylus { + if gdk_button == gdk::BUTTON_PRIMARY + || gdk_button == gdk::BUTTON_SECONDARY + || gdk_button == gdk::BUTTON_MIDDLE + { + handle_pen_event = true; + } + + // again, this is the method to detect proximity on stylus. + if gdk_button == gdk::BUTTON_PRIMARY { + pen_state = PenState::Up; + } else { + pen_state = PenState::Proximity; + } + } else { + #[allow(clippy::collapsible_else_if)] + if gdk_button == gdk::BUTTON_PRIMARY || gdk_button == gdk::BUTTON_SECONDARY { + pen_state = PenState::Up; + handle_pen_event = true; + } + }; + } } gdk::EventType::ProximityIn => { pen_state = PenState::Proximity; @@ -246,27 +258,36 @@ pub(crate) fn handle_key_controller_key_pressed( shortcut_key ); - let (propagation, widget_flags) = if let Some(shortcut_key) = shortcut_key { - canvas - .engine_mut() - .handle_pressed_shortcut_key(shortcut_key, now) - } else { - canvas.engine_mut().handle_pen_event( - PenEvent::KeyPressed { - keyboard_key, - modifier_keys, - }, - None, - now, - ) - }; + match (keyboard_key, canvas.imp().dnd_status.get()) { + (KeyboardKey::ShiftLeft, true) | (KeyboardKey::ShiftRight, true) => { + //we only capture here if there is a drag and drop in progress + canvas.imp().dnd_respect_borders.set(true); + glib::Propagation::Stop + } + _ => { + let (propagation, widget_flags) = if let Some(shortcut_key) = shortcut_key { + canvas + .engine_mut() + .handle_pressed_shortcut_key(shortcut_key, now) + } else { + canvas.engine_mut().handle_pen_event( + PenEvent::KeyPressed { + keyboard_key, + modifier_keys, + }, + None, + now, + ) + }; - canvas.emit_handle_widget_flags(widget_flags); - propagation.into_glib() + canvas.emit_handle_widget_flags(widget_flags); + propagation.into_glib() + } + } } pub(crate) fn handle_key_controller_key_released( - _canvas: &RnCanvas, + canvas: &RnCanvas, gdk_key: gdk::Key, gdk_modifiers: gdk::ModifierType, ) { @@ -275,8 +296,11 @@ pub(crate) fn handle_key_controller_key_released( ); let keyboard_key = retrieve_keyboard_key(gdk_key); - match keyboard_key { - KeyboardKey::ShiftLeft | KeyboardKey::ShiftRight => _canvas.engine_mut().update_dnd(false), + match (keyboard_key, canvas.imp().dnd_status.get()) { + (KeyboardKey::ShiftLeft, true) | (KeyboardKey::ShiftRight, true) => { + // we only remove the modifier if the drag and drop is in progress + canvas.imp().dnd_respect_borders.set(false); + } _ => {} } } diff --git a/crates/rnote-ui/src/canvas/mod.rs b/crates/rnote-ui/src/canvas/mod.rs index b955d77db9..b812f1f4fb 100644 --- a/crates/rnote-ui/src/canvas/mod.rs +++ b/crates/rnote-ui/src/canvas/mod.rs @@ -67,6 +67,10 @@ mod imp { pub(crate) engine: RefCell, pub(crate) engine_task_handler_handle: RefCell>>, + // dnd status + pub(crate) dnd_status: Cell, + pub(crate) dnd_respect_borders: Cell, + pub(crate) output_file: RefCell>, pub(crate) output_file_saved_modified_date_time: RefCell>, pub(crate) output_file_monitor: RefCell>, @@ -162,6 +166,9 @@ mod imp { engine: RefCell::new(engine), engine_task_handler_handle: RefCell::new(None), + dnd_status: Cell::new(false), + dnd_respect_borders: Cell::new(false), + output_file: RefCell::new(None), // is automatically updated whenever the output file changes. output_file_saved_modified_date_time: RefCell::new(None), @@ -1119,15 +1126,28 @@ impl RnCanvas { .build(); // Drop Target - // [OUR DRAG AND DROP PART] - // derived from drop_target + // change the global state for dnd + self.imp().drop_target.connect_enter( + clone!(@weak self as canvas => @default-return gdk::DragAction::COPY, move |_,_,_| { + canvas.imp().dnd_status.set(true); + canvas.imp().dnd_respect_borders.set(false); + gdk::DragAction::COPY + }), + ); + + self.imp().drop_target.connect_leave( + clone!(@weak self as canvas => @default-return (), move |_| { + canvas.imp().dnd_status.set(false); // set the status to false + }), + ); let appwindow_drop_target = self.imp().drop_target.connect_drop( clone!(@weak self as canvas, @weak appwindow => @default-return false, move |_, value, x, y| { let pos = (canvas.engine_ref().camera.transform().inverse() * na::point![x,y]).coords; let mut accept_drop = false; - let respect_border = canvas.engine_ref().dnd; + // should we respect borders ? + let respect_border = canvas.imp().dnd_respect_borders.get(); if value.is::() { // In some scenarios, get() can fail with `UnexpectedNone` even though is() returned true, e.g. when dealing with trashed files. From e5d8c70dd1f24f1ff406e898193cbdb6204c00d5 Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Mon, 19 Feb 2024 22:59:17 +0100 Subject: [PATCH 14/31] re add import though to be superflous --- crates/rnote-ui/src/canvas/input.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/rnote-ui/src/canvas/input.rs b/crates/rnote-ui/src/canvas/input.rs index 8bfb920f9b..b20fc8da3a 100644 --- a/crates/rnote-ui/src/canvas/input.rs +++ b/crates/rnote-ui/src/canvas/input.rs @@ -1,5 +1,6 @@ // Imports use super::RnCanvas; +use adw::glib::subclass::types::ObjectSubclassIsExt; use gtk4::{gdk, glib, graphene, prelude::*, Native}; use rnote_compose::penevent::{KeyboardKey, ModifierKey, PenEvent, PenState, ShortcutKey}; use rnote_compose::penpath::Element; From 776e7b820003fcf9a4a2445721627f439b805d3c Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Tue, 20 Feb 2024 00:41:01 +0100 Subject: [PATCH 15/31] remove debug traces and messages --- .gitignore | 3 +-- crates/rnote-engine/src/engine/import.rs | 4 ++-- crates/rnote-engine/src/pens/selector/penevents.rs | 4 +--- crates/rnote-ui/src/appwindow/actions.rs | 7 +------ crates/rnote-ui/src/canvas/input.rs | 2 +- 5 files changed, 6 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 2c79294853..e227fb0425 100644 --- a/.gitignore +++ b/.gitignore @@ -16,5 +16,4 @@ *.code-workspace .idea/ .toggletasks.json -*.flatpak -IMSIZE.md +*.flatpak \ No newline at end of file diff --git a/crates/rnote-engine/src/engine/import.rs b/crates/rnote-engine/src/engine/import.rs index 2c77bc8c90..bfb888cd36 100644 --- a/crates/rnote-engine/src/engine/import.rs +++ b/crates/rnote-engine/src/engine/import.rs @@ -241,7 +241,7 @@ impl Engine { ) -> oneshot::Receiver> { let (oneshot_sender, oneshot_receiver) = oneshot::channel::>(); - // we get these parameters to enable proper resizing of the windowsœ + // we get these parameters to enable proper resizing of the windows let width_page = self.document.format.width().clone(); let height_page = self.document.format.height().clone(); let is_fixed = self.document.layout.is_fixed_layout(); @@ -257,7 +257,7 @@ impl Engine { height: height_page, isfixed_layout: is_fixed, max_viewpoint: point_max, - respect_borders, + respect_borders: respect_borders, }), ) }; diff --git a/crates/rnote-engine/src/pens/selector/penevents.rs b/crates/rnote-engine/src/pens/selector/penevents.rs index 9ac7bb8ad7..efa31abe3c 100644 --- a/crates/rnote-engine/src/pens/selector/penevents.rs +++ b/crates/rnote-engine/src/pens/selector/penevents.rs @@ -257,7 +257,6 @@ impl Selector { } } // here is ALL of the logic for the resize part - // modifier_keys ? ModifyState::Resize { from_corner, start_bounds, @@ -322,8 +321,7 @@ impl Selector { .maxs(&min_extents) .component_div(&selection_bounds.extents()); - // resize strokes - // THIS WILL REDUCE DOWN TO MUCH LESS HERE + // resize strokes [0] engine_view .store .scale_strokes_with_pivot(selection, scale, pivot); diff --git a/crates/rnote-ui/src/appwindow/actions.rs b/crates/rnote-ui/src/appwindow/actions.rs index 3ac62bb525..1832763a55 100644 --- a/crates/rnote-ui/src/appwindow/actions.rs +++ b/crates/rnote-ui/src/appwindow/actions.rs @@ -865,12 +865,7 @@ pub fn paste_content(appwindow: RnAppWindow, respect_borders: bool) { if !acc.is_empty() { match crate::utils::str_from_u8_nul_utf8(&acc) { Ok(json_string) => { - // debug the json_string - // can I change the StrokeContent - let final_thing = serde_json::from_str::(&json_string.to_string()).unwrap(); - tracing::debug!("{:?}",final_thing); - // only strokes, bounds and background a priori - + // here, stroke content is inserted if let Err(e) = canvas.insert_stroke_content(json_string.to_string()).await { tracing::error!("Failed to insert stroke content while pasting as `{}`, Err: {e:?}", StrokeContent::MIME_TYPE); } diff --git a/crates/rnote-ui/src/canvas/input.rs b/crates/rnote-ui/src/canvas/input.rs index b20fc8da3a..bf91677416 100644 --- a/crates/rnote-ui/src/canvas/input.rs +++ b/crates/rnote-ui/src/canvas/input.rs @@ -21,7 +21,7 @@ pub(crate) fn handle_pointer_controller_event( let mut widget_flags = WidgetFlags::default(); let touch_drawing = canvas.touch_drawing(); let gdk_event_type = event.event_type(); - let gdk_modifiers = event.modifier_state(); // interesting for us + let gdk_modifiers = event.modifier_state(); let _gdk_device = event.device().unwrap(); let backlog_policy = canvas.engine_ref().penholder.backlog_policy(); let is_stylus = event_is_stylus(event); From 6009dd9a546b798247312296e0bb9d6d61cdb672 Mon Sep 17 00:00:00 2001 From: Doublonmousse Date: Tue, 20 Feb 2024 01:13:10 +0100 Subject: [PATCH 16/31] test stylus input --- crates/rnote-ui/src/appwindow/actions.rs | 2 +- crates/rnote-ui/src/canvas/input.rs | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/crates/rnote-ui/src/appwindow/actions.rs b/crates/rnote-ui/src/appwindow/actions.rs index 1832763a55..57ed6503aa 100644 --- a/crates/rnote-ui/src/appwindow/actions.rs +++ b/crates/rnote-ui/src/appwindow/actions.rs @@ -865,7 +865,7 @@ pub fn paste_content(appwindow: RnAppWindow, respect_borders: bool) { if !acc.is_empty() { match crate::utils::str_from_u8_nul_utf8(&acc) { Ok(json_string) => { - // here, stroke content is inserted + // here stroke content is inserted if let Err(e) = canvas.insert_stroke_content(json_string.to_string()).await { tracing::error!("Failed to insert stroke content while pasting as `{}`, Err: {e:?}", StrokeContent::MIME_TYPE); } diff --git a/crates/rnote-ui/src/canvas/input.rs b/crates/rnote-ui/src/canvas/input.rs index bf91677416..e3ca2cbdbb 100644 --- a/crates/rnote-ui/src/canvas/input.rs +++ b/crates/rnote-ui/src/canvas/input.rs @@ -67,8 +67,11 @@ pub(crate) fn handle_pointer_controller_event( let gdk_button = button_event.button(); let mut handle_shortcut_key = false; - // check this with a stylus - tracing::debug!( + // check this with a stylus : not working + // the intent would be to capture if a button on the stylus is pressed + // when the drag and drop occurs but nothing is captured when a drag and + // drop occurs from the pen on windows + tracing::trace!( "canvas event ButtonPress - gdk_button: {gdk_button}, is_stylus: {is_stylus}" ); @@ -110,8 +113,8 @@ pub(crate) fn handle_pointer_controller_event( let button_event = event.downcast_ref::().unwrap(); let gdk_button = button_event.button(); - // check this with a stylus - tracing::debug!( + // check this with a stylus : not working, see below + tracing::tracing!( "canvas event ButtonRelease - gdk_button: {gdk_button}, is_stylus: {is_stylus}" ); From de8533129f5154d8ccc1cae90203278a0be52d45 Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Tue, 20 Feb 2024 01:14:45 +0100 Subject: [PATCH 17/31] test : resizing stroke content (wip) --- IMSIZE.md | 183 +++++++++++++++++++++++ crates/rnote-engine/src/engine/import.rs | 7 + crates/rnote-ui/src/canvas/imexport.rs | 2 +- 3 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 IMSIZE.md diff --git a/IMSIZE.md b/IMSIZE.md new file mode 100644 index 0000000000..7d9615deee --- /dev/null +++ b/IMSIZE.md @@ -0,0 +1,183 @@ +`/crates/rnote-engine/src/engine/import.rs` : + +```rs + pub fn generate_vectorimage_from_bytes( + &self, + pos: na::Vector2, + bytes: Vec, + ) -> oneshot::Receiver> { +``` + +```rs + pub fn generate_bitmapimage_from_bytes( + &self, + pos: na::Vector2, + bytes: Vec, + ) -> oneshot::Receiver> { +``` +In the past implementation, this was the function to send the `format` to the next step in the process, with the `resize` bool as well. + +But now we have two of these functions for svg vs not svg. + +---- +Layout : in `document`, enum `Layout` to find + +`crates/rnote-engine/src/strokes/bitmapimage.rs` + +For the raterized image + +BEWARE : we are interested in `from_image_bytes` and this function is also called elsewhere so defaults needed + + +And + +`crates/rnote-engine/src/strokes/vectorimage.rs/` + +BEWARE : we are interested in `from_svg_str` and this function is also called elsewhere so we need to have defaults as well + +For now we MAY have a correct implementation for the bitmap image only + + +--- +Incorrect thing for the resize caus eonly would work for a single page + +--- +Change the algo : do with respect to the viewport size instead ! + +--- +other imports to see ? +- pdf imports : laisses sans resize ds ts les cas (imports, pas ctrl + v !) +- stroke : always to size +- text : seems to always be 1/2 page by default + +faire un `ctrl+v` special pour respecter les bordures VERTICALES uniquements s'appliquant a un peu plus d'elements, SI BESOIN, sans test pour verifier si l'on + + +For text : temporary `set_text_width` to force the size ? + +For stroke : + +--- +what is going on with `import_generated_content` and the widget resize +- called from `imexport` for `load_in_vectorimage_bytes`, `load_in_bitmapimage_bytes`, `load_in_pdf_bytes` + +Actually just calculating document sizes and width (total NOT format related) +But in this you also find related functions for pages + +--- +Ok so possible to set the custom paste thing for shift ctrl + v but we can't do that for imported images from the user's dragging things inside the rnote windows + +--- +separate logic when pasting a single element from the clipboard +-> change the `ctrl + c` part to into into a single element ? + maybe not, would be possible to do a resize after the element is pasted ? But only for the special ctrl + v case ofc +-> changge at the `ctrl + v` part ? YES, need to adapt it to stroke content (that also includes figures but also a little bit more) + + + +Drag and drop part : ++ modifiers + +Called from `open_file_w_dialogs`. Common with opening file out right. All of the logic is in `crates/rnote-ui/src/canvas/mod.rs` +```rs +/// drop target +``` + + +--- +image fail : from `gen_image` in `crates/rnote-engine/src/render.rs` + + +--- +general_regular_cursor_picker_menubutton for the regular cursor +Shows up in `regular_cursor` from the `RnCanvas` + +file `crates/rnote-ui/src/canvas/mod.rs` + +--- +Transformable : trait from the `crates/rnote-compose/src/transform/transformable.rs` + +Call the `fn scale(&mut self, scale: na::Vector2)` thing on each element. + +But we have to see what happens on multiple selections +`resize_lock_aspectratio` + +```rs +pub struct SelectorConfig { + #[serde(rename = "style")] + pub style: SelectorStyle, + #[serde(rename = "resize_lock_aspectratio")] + pub resize_lock_aspectratio: bool, +} +``` + +Call `handle_pen_event_down` with modifier keys ? From what ? contains `PenEvent` +All of the logic is from `crates/rnote-ui/src/canvas/input.rs` + + +--- +also keyboard events. Can we set them to do a global `ctrl+a` depending on the context that is much more forgiving ? +Issue : still must NOT override the rest (that is not activated for any text field and the like) + +Idea : activated when the TOOLBAR is in focus as well + + +--- +compare the pen event with modifiers and the rest + +When are keyboard events captured for pen events ? What is the secret sauce there making everything work ? + +```rs + // Pointer controller + let pen_state = Cell::new(PenState::Up); + self.pointer_controller.connect_event(clone!(@strong pen_state, @weak obj as canvas => @default-return glib::Propagation::Proceed, move |_, event| { + let (propagation, new_state) = super::input::handle_pointer_controller_event(&canvas, event, pen_state.get()); // event ? + pen_state.set(new_state); + propagation + })); +``` +Gets a pure event and tries +```rs + let gdk_modifiers = event.modifier_state(); +``` +to get the modifiers as well and their states +``` +maybe connect_actions_notify ? +``` + + +```rs + let appwindow_drop_target = self.imp().drop_target.connect_drop( + clone!(@weak self as canvas, @weak appwindow => @default-return false, move |_, value, x, y| { + let pos = (canvas.engine_ref().camera.transform().inverse() * + na::point![x,y]).coords; + let mut accept_drop = false; +``` + +--- +Need to capture both shift AND buttons from the pen if this is a dnd thing as well + + +```rs +let regular_cursor = gdk::Cursor::from_texture( + &gdk::Texture::from_resource( + (String::from(config::APP_IDPATH) + + "icons/scalable/actions/cursor-dot-medium.svg") + .as_str(), + ), + 32, + 32, + gdk::Cursor::from_name("default", None).as_ref(), +); +``` + + +Commit +- captured shift + drag and drop +- now how to capture pen button + drag and drop + - do a connect up, connect down connect drop to set the drag and drop status and invert the relation ? + - wip : some debug traces for stroke content and comments for + +Invert the thing. Activate true for dnd on `connect_enter`, false for dnd on `connect_leave` and take the option on `connect_drop` and reset it + +We reverted it, we check with shift first then go on the next part for the stylus \ No newline at end of file diff --git a/crates/rnote-engine/src/engine/import.rs b/crates/rnote-engine/src/engine/import.rs index bfb888cd36..c536119083 100644 --- a/crates/rnote-engine/src/engine/import.rs +++ b/crates/rnote-engine/src/engine/import.rs @@ -405,6 +405,13 @@ impl Engine { widget_flags |= self.change_pen_style(PenStyle::Selector); let inserted_keys = self.store.insert_stroke_content(content, pos); + + // test : resize these things here + self.store + .scale_strokes_images_with_pivot(&inserted_keys, na::Vector2::new(0.1, 0.1), pos); + self.store + .scale_strokes_with_pivot(&inserted_keys, na::Vector2::new(0.1, 0.1), pos); + self.store.update_geometry_for_strokes(&inserted_keys); self.store.regenerate_rendering_in_viewport_threaded( self.tasks_tx.clone(), diff --git a/crates/rnote-ui/src/canvas/imexport.rs b/crates/rnote-ui/src/canvas/imexport.rs index e18a8a4456..8eddd94d04 100644 --- a/crates/rnote-ui/src/canvas/imexport.rs +++ b/crates/rnote-ui/src/canvas/imexport.rs @@ -174,7 +174,7 @@ impl RnCanvas { }); let content = oneshot_receiver.await??; - tracing::debug!("{:?} {:?}", content.bounds(), content.size()); //calculate bounds + tracing::debug!("{:?} {:?}", content.bounds(), content.size()); //debug trace to see the size of the imported content let widget_flags = self.engine_mut().insert_stroke_content(content, pos); self.emit_handle_widget_flags(widget_flags); From 7ac5392d77a7c50a2e89aa4939c8bccfbcbd55f6 Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Tue, 20 Feb 2024 01:14:45 +0100 Subject: [PATCH 18/31] test : resizing stroke content (wip) --- crates/rnote-engine/src/engine/import.rs | 7 +++++++ crates/rnote-engine/src/pens/selector/penevents.rs | 2 +- crates/rnote-ui/src/canvas/imexport.rs | 2 +- crates/rnote-ui/src/canvas/input.rs | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/crates/rnote-engine/src/engine/import.rs b/crates/rnote-engine/src/engine/import.rs index bfb888cd36..efb2013d56 100644 --- a/crates/rnote-engine/src/engine/import.rs +++ b/crates/rnote-engine/src/engine/import.rs @@ -405,6 +405,13 @@ impl Engine { widget_flags |= self.change_pen_style(PenStyle::Selector); let inserted_keys = self.store.insert_stroke_content(content, pos); + + // test : resize these things here + self.store + .scale_strokes_with_pivot(&inserted_keys, na::Vector2::new(0.1, 0.1), pos); + self.store + .scale_strokes_images_with_pivot(&inserted_keys, na::Vector2::new(0.1, 0.1), pos); + self.store.update_geometry_for_strokes(&inserted_keys); self.store.regenerate_rendering_in_viewport_threaded( self.tasks_tx.clone(), diff --git a/crates/rnote-engine/src/pens/selector/penevents.rs b/crates/rnote-engine/src/pens/selector/penevents.rs index efa31abe3c..31fed75608 100644 --- a/crates/rnote-engine/src/pens/selector/penevents.rs +++ b/crates/rnote-engine/src/pens/selector/penevents.rs @@ -321,7 +321,7 @@ impl Selector { .maxs(&min_extents) .component_div(&selection_bounds.extents()); - // resize strokes [0] + // resize strokes engine_view .store .scale_strokes_with_pivot(selection, scale, pivot); diff --git a/crates/rnote-ui/src/canvas/imexport.rs b/crates/rnote-ui/src/canvas/imexport.rs index e18a8a4456..8eddd94d04 100644 --- a/crates/rnote-ui/src/canvas/imexport.rs +++ b/crates/rnote-ui/src/canvas/imexport.rs @@ -174,7 +174,7 @@ impl RnCanvas { }); let content = oneshot_receiver.await??; - tracing::debug!("{:?} {:?}", content.bounds(), content.size()); //calculate bounds + tracing::debug!("{:?} {:?}", content.bounds(), content.size()); //debug trace to see the size of the imported content let widget_flags = self.engine_mut().insert_stroke_content(content, pos); self.emit_handle_widget_flags(widget_flags); diff --git a/crates/rnote-ui/src/canvas/input.rs b/crates/rnote-ui/src/canvas/input.rs index e3ca2cbdbb..f11e978b94 100644 --- a/crates/rnote-ui/src/canvas/input.rs +++ b/crates/rnote-ui/src/canvas/input.rs @@ -114,7 +114,7 @@ pub(crate) fn handle_pointer_controller_event( let gdk_button = button_event.button(); // check this with a stylus : not working, see below - tracing::tracing!( + tracing::trace!( "canvas event ButtonRelease - gdk_button: {gdk_button}, is_stylus: {is_stylus}" ); From 73c7b967f3896b74dfcfdbcf738876e1229e81eb Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Tue, 20 Feb 2024 01:56:34 +0100 Subject: [PATCH 19/31] Delete IMSIZE.md --- IMSIZE.md | 183 ------------------------------------------------------ 1 file changed, 183 deletions(-) delete mode 100644 IMSIZE.md diff --git a/IMSIZE.md b/IMSIZE.md deleted file mode 100644 index 7d9615deee..0000000000 --- a/IMSIZE.md +++ /dev/null @@ -1,183 +0,0 @@ -`/crates/rnote-engine/src/engine/import.rs` : - -```rs - pub fn generate_vectorimage_from_bytes( - &self, - pos: na::Vector2, - bytes: Vec, - ) -> oneshot::Receiver> { -``` - -```rs - pub fn generate_bitmapimage_from_bytes( - &self, - pos: na::Vector2, - bytes: Vec, - ) -> oneshot::Receiver> { -``` -In the past implementation, this was the function to send the `format` to the next step in the process, with the `resize` bool as well. - -But now we have two of these functions for svg vs not svg. - ----- -Layout : in `document`, enum `Layout` to find - -`crates/rnote-engine/src/strokes/bitmapimage.rs` - -For the raterized image - -BEWARE : we are interested in `from_image_bytes` and this function is also called elsewhere so defaults needed - - -And - -`crates/rnote-engine/src/strokes/vectorimage.rs/` - -BEWARE : we are interested in `from_svg_str` and this function is also called elsewhere so we need to have defaults as well - -For now we MAY have a correct implementation for the bitmap image only - - ---- -Incorrect thing for the resize caus eonly would work for a single page - ---- -Change the algo : do with respect to the viewport size instead ! - ---- -other imports to see ? -- pdf imports : laisses sans resize ds ts les cas (imports, pas ctrl + v !) -- stroke : always to size -- text : seems to always be 1/2 page by default - -faire un `ctrl+v` special pour respecter les bordures VERTICALES uniquements s'appliquant a un peu plus d'elements, SI BESOIN, sans test pour verifier si l'on - - -For text : temporary `set_text_width` to force the size ? - -For stroke : - ---- -what is going on with `import_generated_content` and the widget resize -- called from `imexport` for `load_in_vectorimage_bytes`, `load_in_bitmapimage_bytes`, `load_in_pdf_bytes` - -Actually just calculating document sizes and width (total NOT format related) -But in this you also find related functions for pages - ---- -Ok so possible to set the custom paste thing for shift ctrl + v but we can't do that for imported images from the user's dragging things inside the rnote windows - ---- -separate logic when pasting a single element from the clipboard --> change the `ctrl + c` part to into into a single element ? - maybe not, would be possible to do a resize after the element is pasted ? But only for the special ctrl + v case ofc --> changge at the `ctrl + v` part ? YES, need to adapt it to stroke content (that also includes figures but also a little bit more) - - - -Drag and drop part : -+ modifiers - -Called from `open_file_w_dialogs`. Common with opening file out right. All of the logic is in `crates/rnote-ui/src/canvas/mod.rs` -```rs -/// drop target -``` - - ---- -image fail : from `gen_image` in `crates/rnote-engine/src/render.rs` - - ---- -general_regular_cursor_picker_menubutton for the regular cursor -Shows up in `regular_cursor` from the `RnCanvas` - -file `crates/rnote-ui/src/canvas/mod.rs` - ---- -Transformable : trait from the `crates/rnote-compose/src/transform/transformable.rs` - -Call the `fn scale(&mut self, scale: na::Vector2)` thing on each element. - -But we have to see what happens on multiple selections -`resize_lock_aspectratio` - -```rs -pub struct SelectorConfig { - #[serde(rename = "style")] - pub style: SelectorStyle, - #[serde(rename = "resize_lock_aspectratio")] - pub resize_lock_aspectratio: bool, -} -``` - -Call `handle_pen_event_down` with modifier keys ? From what ? contains `PenEvent` -All of the logic is from `crates/rnote-ui/src/canvas/input.rs` - - ---- -also keyboard events. Can we set them to do a global `ctrl+a` depending on the context that is much more forgiving ? -Issue : still must NOT override the rest (that is not activated for any text field and the like) - -Idea : activated when the TOOLBAR is in focus as well - - ---- -compare the pen event with modifiers and the rest - -When are keyboard events captured for pen events ? What is the secret sauce there making everything work ? - -```rs - // Pointer controller - let pen_state = Cell::new(PenState::Up); - self.pointer_controller.connect_event(clone!(@strong pen_state, @weak obj as canvas => @default-return glib::Propagation::Proceed, move |_, event| { - let (propagation, new_state) = super::input::handle_pointer_controller_event(&canvas, event, pen_state.get()); // event ? - pen_state.set(new_state); - propagation - })); -``` -Gets a pure event and tries -```rs - let gdk_modifiers = event.modifier_state(); -``` -to get the modifiers as well and their states -``` -maybe connect_actions_notify ? -``` - - -```rs - let appwindow_drop_target = self.imp().drop_target.connect_drop( - clone!(@weak self as canvas, @weak appwindow => @default-return false, move |_, value, x, y| { - let pos = (canvas.engine_ref().camera.transform().inverse() * - na::point![x,y]).coords; - let mut accept_drop = false; -``` - ---- -Need to capture both shift AND buttons from the pen if this is a dnd thing as well - - -```rs -let regular_cursor = gdk::Cursor::from_texture( - &gdk::Texture::from_resource( - (String::from(config::APP_IDPATH) - + "icons/scalable/actions/cursor-dot-medium.svg") - .as_str(), - ), - 32, - 32, - gdk::Cursor::from_name("default", None).as_ref(), -); -``` - - -Commit -- captured shift + drag and drop -- now how to capture pen button + drag and drop - - do a connect up, connect down connect drop to set the drag and drop status and invert the relation ? - - wip : some debug traces for stroke content and comments for - -Invert the thing. Activate true for dnd on `connect_enter`, false for dnd on `connect_leave` and take the option on `connect_drop` and reset it - -We reverted it, we check with shift first then go on the next part for the stylus \ No newline at end of file From 8ec0fe8ba7ef61332c99584a8c0b13f93c12d2a9 Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Tue, 20 Feb 2024 02:28:36 +0100 Subject: [PATCH 20/31] special paste for strokes --- crates/rnote-engine/src/engine/import.rs | 29 ++++++++++-- .../src/pens/selector/penevents.rs | 1 - crates/rnote-ui/src/appwindow/actions.rs | 45 +++++++++---------- crates/rnote-ui/src/canvas/imexport.rs | 11 ++++- crates/rnote-ui/src/canvas/input.rs | 15 +------ 5 files changed, 57 insertions(+), 44 deletions(-) diff --git a/crates/rnote-engine/src/engine/import.rs b/crates/rnote-engine/src/engine/import.rs index efb2013d56..a9a721fbc4 100644 --- a/crates/rnote-engine/src/engine/import.rs +++ b/crates/rnote-engine/src/engine/import.rs @@ -4,6 +4,7 @@ use crate::pens::Pen; use crate::pens::PenStyle; use crate::store::chrono_comp::StrokeLayer; use crate::store::StrokeKey; +use crate::strokes::resize::calculate_resize_ratio; use crate::strokes::resize::ImageSizeOption; use crate::strokes::Resize; use crate::strokes::{BitmapImage, Stroke, VectorImage}; @@ -395,6 +396,7 @@ impl Engine { &mut self, content: StrokeContent, pos: na::Vector2, + resize: ImageSizeOption, ) -> WidgetFlags { let mut widget_flags = WidgetFlags::default(); @@ -404,14 +406,32 @@ impl Engine { self.store.set_selected_keys(&all_strokes, false); widget_flags |= self.change_pen_style(PenStyle::Selector); + // calculate ratio + let ratio = match resize { + ImageSizeOption::ResizeImage(resize) => { + calculate_resize_ratio(resize, content.size().unwrap(), pos) + } + _ => 1.0f64, + }; let inserted_keys = self.store.insert_stroke_content(content, pos); - // test : resize these things here - self.store - .scale_strokes_with_pivot(&inserted_keys, na::Vector2::new(0.1, 0.1), pos); + self.store.update_geometry_for_strokes(&inserted_keys); + self.store.regenerate_rendering_in_viewport_threaded( + self.tasks_tx.clone(), + false, + self.camera.viewport(), + self.camera.image_scale(), + ); + self.store - .scale_strokes_images_with_pivot(&inserted_keys, na::Vector2::new(0.1, 0.1), pos); + .scale_strokes_with_pivot(&inserted_keys, na::Vector2::new(ratio, ratio), pos); + self.store.scale_strokes_images_with_pivot( + &inserted_keys, + na::Vector2::new(ratio, ratio), + pos, + ); + // re generate view self.store.update_geometry_for_strokes(&inserted_keys); self.store.regenerate_rendering_in_viewport_threaded( self.tasks_tx.clone(), @@ -419,6 +439,7 @@ impl Engine { self.camera.viewport(), self.camera.image_scale(), ); + widget_flags |= self.penholder.current_pen_update_state(&mut EngineViewMut { tasks_tx: self.tasks_tx.clone(), pens_config: &mut self.pens_config, diff --git a/crates/rnote-engine/src/pens/selector/penevents.rs b/crates/rnote-engine/src/pens/selector/penevents.rs index 31fed75608..c009942c16 100644 --- a/crates/rnote-engine/src/pens/selector/penevents.rs +++ b/crates/rnote-engine/src/pens/selector/penevents.rs @@ -256,7 +256,6 @@ impl Selector { *current_rotation_angle = new_rotation_angle; } } - // here is ALL of the logic for the resize part ModifyState::Resize { from_corner, start_bounds, diff --git a/crates/rnote-ui/src/appwindow/actions.rs b/crates/rnote-ui/src/appwindow/actions.rs index 57ed6503aa..d0f43627c9 100644 --- a/crates/rnote-ui/src/appwindow/actions.rs +++ b/crates/rnote-ui/src/appwindow/actions.rs @@ -10,6 +10,7 @@ use rnote_compose::penevent::ShortcutKey; use rnote_compose::SplitOrder; use rnote_engine::engine::StrokeContent; use rnote_engine::pens::PenStyle; +use rnote_engine::strokes::resize::{ImageSizeOption, Resize}; use rnote_engine::{Camera, Engine}; use std::path::PathBuf; use std::str::FromStr; @@ -685,26 +686,6 @@ impl RnAppWindow { return; } }; - // // check the content length here (3) ? - // // stroke, then the xml then the image ? - // let debug_types=content.iter().map(|(_data,mime_type)|-> &str { - // mime_type.as_str() - // }).collect::>(); - // tracing::debug!("{:?}",debug_types); - - - // let mut stringify=content[0..content.len()-1].iter().map(|(data,_)| {String::from_utf8_lossy(data)}); - // // check what is in the stroke data part - // tracing::debug!("stroke data {:?}",stringify.next()); - - // // xml part ? - // tracing::debug!("xml part {:?}",stringify.next()); - - // // jpg part ? - // // tracing::debug!("jpg part {:?}",stringify.next()); - // let image = render::Image::try_from_encoded_bytes(content.iter().map(|(x,_)| {x}).last().unwrap()).unwrap(); - // tracing::debug!("image part rect \t{:?}\n pixel width\t{:?}\n pixel height\t{:?}\n memory format\t{:?}\n",image.rect,image.pixel_width,image.pixel_height,image.memory_format); - let gdk_content_provider = gdk::ContentProvider::new_union(content.into_iter().map(|(data, mime_type)| { gdk::ContentProvider::for_bytes(mime_type.as_str(), &glib::Bytes::from_owned(data)) @@ -805,8 +786,6 @@ pub fn paste_content(appwindow: RnAppWindow, respect_borders: bool) { let canvas = appwindow.active_tab_wrapper().canvas(); let content_formats = appwindow.clipboard().formats(); - // tracing::debug!("{:?}",content_formats); - // Order matters here, we want to go from specific -> generic, mostly because `text/plain` is contained in other text based formats if content_formats.contain_mime_type("text/uri-list") { glib::spawn_future_local(clone!(@weak appwindow => async move { @@ -865,8 +844,26 @@ pub fn paste_content(appwindow: RnAppWindow, respect_borders: bool) { if !acc.is_empty() { match crate::utils::str_from_u8_nul_utf8(&acc) { Ok(json_string) => { - // here stroke content is inserted - if let Err(e) = canvas.insert_stroke_content(json_string.to_string()).await { + let resize_argument = match respect_borders { + false => ImageSizeOption::RespectOriginalSize, + true => { + // get all info if resizing has to be done + let width_page = canvas.engine_ref().document.format.width().clone(); + let height_page = canvas.engine_ref().document.format.height().clone(); + let is_fixed = canvas.engine_ref().document.layout.is_fixed_layout(); + let point_max: na::OPoint> = canvas.engine_ref().camera.viewport().maxs; + + ImageSizeOption::ResizeImage(Resize { + width: width_page, + height: height_page, + isfixed_layout: is_fixed, + max_viewpoint: point_max, + respect_borders: respect_borders, + }) + } + }; + + if let Err(e) = canvas.insert_stroke_content(json_string.to_string(),resize_argument).await { tracing::error!("Failed to insert stroke content while pasting as `{}`, Err: {e:?}", StrokeContent::MIME_TYPE); } } diff --git a/crates/rnote-ui/src/canvas/imexport.rs b/crates/rnote-ui/src/canvas/imexport.rs index 8eddd94d04..dfda7abacb 100644 --- a/crates/rnote-ui/src/canvas/imexport.rs +++ b/crates/rnote-ui/src/canvas/imexport.rs @@ -5,6 +5,7 @@ use gtk4::{gio, prelude::*}; use rnote_compose::ext::Vector2Ext; use rnote_engine::engine::export::{DocExportPrefs, DocPagesExportPrefs, SelectionExportPrefs}; use rnote_engine::engine::{EngineSnapshot, StrokeContent}; +use rnote_engine::strokes::resize::ImageSizeOption; use rnote_engine::strokes::Stroke; use rnote_engine::WidgetFlags; use std::ops::Range; @@ -157,7 +158,11 @@ impl RnCanvas { /// Deserializes the stroke content and inserts it into the engine. /// /// The data is usually coming from the clipboard, drop source, etc. - pub(crate) async fn insert_stroke_content(&self, json_string: String) -> anyhow::Result<()> { + pub(crate) async fn insert_stroke_content( + &self, + json_string: String, + resize: ImageSizeOption, + ) -> anyhow::Result<()> { let (oneshot_sender, oneshot_receiver) = oneshot::channel::>(); let pos = self.determine_stroke_import_pos(None); @@ -175,7 +180,9 @@ impl RnCanvas { let content = oneshot_receiver.await??; tracing::debug!("{:?} {:?}", content.bounds(), content.size()); //debug trace to see the size of the imported content - let widget_flags = self.engine_mut().insert_stroke_content(content, pos); + let widget_flags = self + .engine_mut() + .insert_stroke_content(content, pos, resize); self.emit_handle_widget_flags(widget_flags); Ok(()) diff --git a/crates/rnote-ui/src/canvas/input.rs b/crates/rnote-ui/src/canvas/input.rs index f11e978b94..3f060eff48 100644 --- a/crates/rnote-ui/src/canvas/input.rs +++ b/crates/rnote-ui/src/canvas/input.rs @@ -241,7 +241,7 @@ pub(crate) fn handle_key_controller_key_pressed( gdk_key: gdk::Key, gdk_modifiers: gdk::ModifierType, ) -> glib::Propagation { - tracing::debug!( + tracing::trace!( "canvas event key pressed - gdk_key: {gdk_key:?}, gdk_modifiers: {gdk_modifiers:?}" ); canvas.grab_focus(); @@ -251,17 +251,6 @@ pub(crate) fn handle_key_controller_key_pressed( let modifier_keys = retrieve_modifier_keys(gdk_modifiers); let shortcut_key = retrieve_keyboard_shortcut_key(gdk_key, gdk_modifiers); - // re print all of this information once again - tracing::debug!( - "key\t {:?} - modifier\t {:?} - shortcut\t {:?} - ", - keyboard_key, - modifier_keys, - shortcut_key - ); - match (keyboard_key, canvas.imp().dnd_status.get()) { (KeyboardKey::ShiftLeft, true) | (KeyboardKey::ShiftRight, true) => { //we only capture here if there is a drag and drop in progress @@ -295,7 +284,7 @@ pub(crate) fn handle_key_controller_key_released( gdk_key: gdk::Key, gdk_modifiers: gdk::ModifierType, ) { - tracing::debug!( + tracing::trace!( "canvas event key released - gdk_key: {gdk_key:?}, gdk_modifiers: {gdk_modifiers:?}" ); let keyboard_key = retrieve_keyboard_key(gdk_key); From f8a93d61763f6d484e8e8e3891d22efd4d59ee71 Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Tue, 20 Feb 2024 12:21:09 +0100 Subject: [PATCH 21/31] merge corrections (wip) --- crates/rnote-ui/data/ui/shortcuts.ui | 6 ++++++ crates/rnote-ui/src/appwindow/actions.rs | 10 +++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/crates/rnote-ui/data/ui/shortcuts.ui b/crates/rnote-ui/data/ui/shortcuts.ui index 2e9b227875..9dd12fcb9c 100644 --- a/crates/rnote-ui/data/ui/shortcuts.ui +++ b/crates/rnote-ui/data/ui/shortcuts.ui @@ -231,6 +231,12 @@ <ctrl>v + + + Paste Clipboard whilst respecting borders + <ctrl><shift>z + + Duplicate Selection diff --git a/crates/rnote-ui/src/appwindow/actions.rs b/crates/rnote-ui/src/appwindow/actions.rs index 0965baeab5..24992f5f52 100644 --- a/crates/rnote-ui/src/appwindow/actions.rs +++ b/crates/rnote-ui/src/appwindow/actions.rs @@ -825,7 +825,7 @@ impl RnAppWindow { }).collect::>(); for file_path in file_paths { - appwindow.open_file_w_dialogs(gio::File::for_path(&file_path), target_pos, true).await; + appwindow.open_file_w_dialogs(gio::File::for_path(&file_path), target_pos, false,false).await; } } Ok(None) => {} @@ -861,7 +861,7 @@ impl RnAppWindow { if !acc.is_empty() { match crate::utils::str_from_u8_nul_utf8(&acc) { Ok(json_string) => { - if let Err(e) = canvas.insert_stroke_content(json_string.to_string(), target_pos).await { + if let Err(e) = canvas.insert_stroke_content(json_string.to_string(), ImageSizeOption::RespectOriginalSize,target_pos).await { tracing::error!("Failed to insert stroke content while pasting as `{}`, Err: {e:?}", StrokeContent::MIME_TYPE); } } @@ -903,7 +903,7 @@ impl RnAppWindow { if !acc.is_empty() { match crate::utils::str_from_u8_nul_utf8(&acc) { Ok(text) => { - if let Err(e) = canvas.load_in_vectorimage_bytes(text.as_bytes().to_vec(), target_pos).await { + if let Err(e) = canvas.load_in_vectorimage_bytes(text.as_bytes().to_vec(), target_pos,false).await { tracing::error!( "Loading VectorImage bytes failed while pasting as Svg failed, Err: {e:?}" ); @@ -941,7 +941,7 @@ impl RnAppWindow { match appwindow.clipboard().read_texture_future().await { Ok(Some(texture)) => { - if let Err(e) = canvas.load_in_bitmapimage_bytes(texture.save_to_png_bytes().to_vec(), target_pos).await { + if let Err(e) = canvas.load_in_bitmapimage_bytes(texture.save_to_png_bytes().to_vec(), target_pos,false).await { tracing::error!( "Loading bitmap image bytes failed while pasting clipboard as {mime_type}, Err: {e:?}" ); @@ -1072,7 +1072,7 @@ pub fn paste_content(appwindow: RnAppWindow, respect_borders: bool) { } }; - if let Err(e) = canvas.insert_stroke_content(json_string.to_string(),resize_argument).await { + if let Err(e) = canvas.insert_stroke_content(json_string.to_string(),resize_argument,None).await { //may be wrong to set to None tracing::error!("Failed to insert stroke content while pasting as `{}`, Err: {e:?}", StrokeContent::MIME_TYPE); } } From 2797cdac4cc4e6150e0ba22adb915d52f9b6b3a0 Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Tue, 20 Feb 2024 13:12:05 +0100 Subject: [PATCH 22/31] remove duplications --- crates/rnote-ui/data/ui/contextmenu.ui | 4 + crates/rnote-ui/src/appwindow/actions.rs | 263 ++++------------------- 2 files changed, 49 insertions(+), 218 deletions(-) diff --git a/crates/rnote-ui/data/ui/contextmenu.ui b/crates/rnote-ui/data/ui/contextmenu.ui index c49ab3b14d..8734c69339 100644 --- a/crates/rnote-ui/data/ui/contextmenu.ui +++ b/crates/rnote-ui/data/ui/contextmenu.ui @@ -18,6 +18,10 @@ _Paste win.clipboard-paste-contextmenu + + _Paste whilst respecting borders + win.clipboard-paste-contextmenu-special + diff --git a/crates/rnote-ui/src/appwindow/actions.rs b/crates/rnote-ui/src/appwindow/actions.rs index 24992f5f52..51248fed19 100644 --- a/crates/rnote-ui/src/appwindow/actions.rs +++ b/crates/rnote-ui/src/appwindow/actions.rs @@ -152,6 +152,9 @@ impl RnAppWindow { let action_clipboard_paste_contextmenu = gio::SimpleAction::new("clipboard-paste-contextmenu", None); self.add_action(&action_clipboard_paste_contextmenu); + let action_clipboard_paste_contextmenu_special = + gio::SimpleAction::new("clipboard-paste-contextmenu-special", None); + self.add_action(&action_clipboard_paste_contextmenu_special); let action_active_tab_move_left = gio::SimpleAction::new("active-tab-move-left", None); self.add_action(&action_active_tab_move_left); let action_active_tab_move_right = gio::SimpleAction::new("active-tab-move-right", None); @@ -735,11 +738,11 @@ impl RnAppWindow { // the logic has been moved to paste_content to make it possible to add a special paste method action_clipboard_respect_borders.connect_activate( clone!(@weak self as appwindow => move |_, _| { - paste_content(appwindow,true); + appwindow.clipboard_paste(None,true); }), ); action_clipboard_paste.connect_activate(clone!(@weak self as appwindow => move |_, _| { - paste_content(appwindow,false); + appwindow.clipboard_paste(None,false); })); action_clipboard_paste_contextmenu.connect_activate( @@ -754,7 +757,23 @@ impl RnAppWindow { .coords }); - appwindow.clipboard_paste(last_contextmenu_pos); + appwindow.clipboard_paste(last_contextmenu_pos,false); + }), + ); + + action_clipboard_paste_contextmenu_special.connect_activate( + clone!(@weak self as appwindow => move |_, _| { + let canvas_wrapper = appwindow.active_tab_wrapper(); + let canvas = canvas_wrapper.canvas(); + + let last_contextmenu_pos = canvas_wrapper.last_contextmenu_pos().map(|vec2| { + let p = graphene::Point::new(vec2.x as f32, vec2.y as f32); + (canvas.engine_ref().camera.transform().inverse() + * na::point![p.x() as f64, p.y() as f64]) + .coords + }); + + appwindow.clipboard_paste(last_contextmenu_pos,true); }), ); } @@ -798,7 +817,7 @@ impl RnAppWindow { } } - fn clipboard_paste(&self, target_pos: Option>) { + fn clipboard_paste(&self, target_pos: Option>, respect_borders: bool) { let canvas_wrapper = self.active_tab_wrapper(); let canvas = canvas_wrapper.canvas(); let content_formats = self.clipboard().formats(); @@ -825,7 +844,7 @@ impl RnAppWindow { }).collect::>(); for file_path in file_paths { - appwindow.open_file_w_dialogs(gio::File::for_path(&file_path), target_pos, false,false).await; + appwindow.open_file_w_dialogs(gio::File::for_path(&file_path), target_pos, false,respect_borders).await; } } Ok(None) => {} @@ -861,7 +880,25 @@ impl RnAppWindow { if !acc.is_empty() { match crate::utils::str_from_u8_nul_utf8(&acc) { Ok(json_string) => { - if let Err(e) = canvas.insert_stroke_content(json_string.to_string(), ImageSizeOption::RespectOriginalSize,target_pos).await { + let resize_argument = match respect_borders { + false => ImageSizeOption::RespectOriginalSize, + true => { + // get all info if resizing has to be done + let width_page = canvas.engine_ref().document.format.width().clone(); + let height_page = canvas.engine_ref().document.format.height().clone(); + let is_fixed = canvas.engine_ref().document.layout.is_fixed_layout(); + let point_max: na::OPoint> = canvas.engine_ref().camera.viewport().maxs; + + ImageSizeOption::ResizeImage(Resize { + width: width_page, + height: height_page, + isfixed_layout: is_fixed, + max_viewpoint: point_max, + respect_borders: respect_borders, + }) + } + }; + if let Err(e) = canvas.insert_stroke_content(json_string.to_string(), resize_argument,target_pos).await { tracing::error!("Failed to insert stroke content while pasting as `{}`, Err: {e:?}", StrokeContent::MIME_TYPE); } } @@ -903,7 +940,7 @@ impl RnAppWindow { if !acc.is_empty() { match crate::utils::str_from_u8_nul_utf8(&acc) { Ok(text) => { - if let Err(e) = canvas.load_in_vectorimage_bytes(text.as_bytes().to_vec(), target_pos,false).await { + if let Err(e) = canvas.load_in_vectorimage_bytes(text.as_bytes().to_vec(), target_pos,respect_borders).await { tracing::error!( "Loading VectorImage bytes failed while pasting as Svg failed, Err: {e:?}" ); @@ -941,7 +978,7 @@ impl RnAppWindow { match appwindow.clipboard().read_texture_future().await { Ok(Some(texture)) => { - if let Err(e) = canvas.load_in_bitmapimage_bytes(texture.save_to_png_bytes().to_vec(), target_pos,false).await { + if let Err(e) = canvas.load_in_bitmapimage_bytes(texture.save_to_png_bytes().to_vec(), target_pos,respect_borders).await { tracing::error!( "Loading bitmap image bytes failed while pasting clipboard as {mime_type}, Err: {e:?}" ); @@ -985,213 +1022,3 @@ impl RnAppWindow { } } } - -/// for now we end up with two different function with the same function and two different additional arguments, this can be cleaned up further ... - -/// logic for pasting content from the clipboard -/// the extra argument `respect_borders` is to change the logic -/// when importing images (for now, limited to only that) -pub fn paste_content(appwindow: RnAppWindow, respect_borders: bool) { - let canvas = appwindow.active_tab_wrapper().canvas(); - let content_formats = appwindow.clipboard().formats(); - - // Order matters here, we want to go from specific -> generic, mostly because `text/plain` is contained in other text based formats - if content_formats.contain_mime_type("text/uri-list") { - glib::spawn_future_local(clone!(@weak appwindow => async move { - tracing::debug!("Recognized clipboard content format: files list"); - - match appwindow.clipboard().read_text_future().await { - Ok(Some(text)) => { - let file_paths = text.lines().filter_map(|line| { - let file_path = if let Ok(path_uri) = url::Url::parse(line) { - path_uri.to_file_path().ok()? - } else { - PathBuf::from(&line) - }; - - if file_path.exists() { - Some(file_path) - } else { - None - } - }).collect::>(); - - for file_path in file_paths { - appwindow.open_file_w_dialogs(gio::File::for_path(&file_path), None, true,respect_borders).await; - } - } - Ok(None) => {} - Err(e) => { - tracing::error!("Reading clipboard text while pasting clipboard from path failed, Err: {e:?}"); - - } - } - })); - } else if content_formats.contain_mime_type(StrokeContent::MIME_TYPE) { - glib::spawn_future_local(clone!(@weak canvas, @weak appwindow => async move { - tracing::debug!("Recognized clipboard content format: {}", StrokeContent::MIME_TYPE); - - match appwindow.clipboard().read_future(&[StrokeContent::MIME_TYPE], glib::source::Priority::DEFAULT).await { - Ok((input_stream, _)) => { - let mut acc = Vec::new(); - loop { - match input_stream.read_future(vec![0; CLIPBOARD_INPUT_STREAM_BUFSIZE], glib::source::Priority::DEFAULT).await { - Ok((mut bytes, n)) => { - if n == 0 { - break; - } - acc.append(&mut bytes); - } - Err(e) => { - tracing::error!("Failed to read clipboard input stream, Err: {e:?}"); - acc.clear(); - break; - } - } - } - - if !acc.is_empty() { - match crate::utils::str_from_u8_nul_utf8(&acc) { - Ok(json_string) => { - let resize_argument = match respect_borders { - false => ImageSizeOption::RespectOriginalSize, - true => { - // get all info if resizing has to be done - let width_page = canvas.engine_ref().document.format.width().clone(); - let height_page = canvas.engine_ref().document.format.height().clone(); - let is_fixed = canvas.engine_ref().document.layout.is_fixed_layout(); - let point_max: na::OPoint> = canvas.engine_ref().camera.viewport().maxs; - - ImageSizeOption::ResizeImage(Resize { - width: width_page, - height: height_page, - isfixed_layout: is_fixed, - max_viewpoint: point_max, - respect_borders: respect_borders, - }) - } - }; - - if let Err(e) = canvas.insert_stroke_content(json_string.to_string(),resize_argument,None).await { //may be wrong to set to None - tracing::error!("Failed to insert stroke content while pasting as `{}`, Err: {e:?}", StrokeContent::MIME_TYPE); - } - } - Err(e) => tracing::error!("Failed to read stroke content &str from clipboard data, Err: {e:?}"), - } - } - } - Err(e) => { - tracing::error!( - "Reading clipboard failed while pasting as `{}`, Err: {e:?}", - StrokeContent::MIME_TYPE - ); - } - }; - })); - } else if content_formats.contain_mime_type("image/svg+xml") { - glib::spawn_future_local(clone!(@weak appwindow => async move { - tracing::debug!("Recognized clipboard content: svg image"); - - match appwindow.clipboard().read_future(&["image/svg+xml"], glib::source::Priority::DEFAULT).await { - Ok((input_stream, _)) => { - let mut acc = Vec::new(); - loop { - match input_stream.read_future(vec![0; CLIPBOARD_INPUT_STREAM_BUFSIZE], glib::source::Priority::DEFAULT).await { - Ok((mut bytes, n)) => { - if n == 0 { - break; - } - acc.append(&mut bytes); - } - Err(e) => { - tracing::error!("Failed to read clipboard input stream while pasting as Svg, Err: {e:?}"); - acc.clear(); - break; - } - } - } - - if !acc.is_empty() { - match crate::utils::str_from_u8_nul_utf8(&acc) { - Ok(text) => { - // need to pass the respect_border to `load_in_vectorimage_bytes` - if let Err(e) = canvas.load_in_vectorimage_bytes(text.as_bytes().to_vec(), None, respect_borders).await { - tracing::error!( - "Loading VectorImage bytes failed while pasting as Svg failed, Err: {e:?}" - ); - }; - } - Err(e) => tracing::error!("Failed to get string from clipboard data while pasting as Svg, Err: {e:?}"), - } - } - } - Err(e) => { - tracing::error!("Failed to read clipboard data while pasting as Svg, Err: {e:?}"); - } - }; - })); - } else if content_formats.contain_mime_type("image/png") - || content_formats.contain_mime_type("image/jpeg") - || content_formats.contain_mime_type("image/jpg") - || content_formats.contain_mime_type("image/tiff") - || content_formats.contain_mime_type("image/bmp") - { - const MIMES: [&str; 5] = [ - "image/png", - "image/jpeg", - "image/jpg", - "image/tiff", - "image/bmp", - ]; - if let Some(mime_type) = MIMES - .into_iter() - .find(|&mime| content_formats.contain_mime_type(mime)) - { - glib::spawn_future_local(clone!(@weak canvas, @weak appwindow => async move { - tracing::debug!("Recognized clipboard content: bitmap image"); - - match appwindow.clipboard().read_texture_future().await { - Ok(Some(texture)) => { - if let Err(e) = canvas.load_in_bitmapimage_bytes(texture.save_to_png_bytes().to_vec(), None,respect_borders).await { - tracing::error!( - "Loading bitmap image bytes failed while pasting clipboard as {mime_type}, Err: {e:?}" - ); - }; - } - Ok(None) => {} - Err(e) => { - tracing::error!( - "Reading clipboard text failed while pasting clipboard as {mime_type}, Err: {e:?}" - ); - } - }; - })); - } - } else if content_formats.contain_mime_type("text/plain") - || content_formats.contain_mime_type("text/plain;charset=utf-8") - { - glib::spawn_future_local(clone!(@weak canvas, @weak appwindow => async move { - tracing::debug!("Recognized clipboard content: plain text"); - - match appwindow.clipboard().read_text_future().await { - Ok(Some(text)) => { - if let Err(e) = canvas.load_in_text(text.to_string(), None) { - tracing::error!("Failed to paste clipboard text, Err: {e:?}"); - } - } - Ok(None) => {} - Err(e) => { - tracing::error!( - "Reading clipboard text failed while pasting clipboard as plain text, Err: {e:?}" - ); - - } - } - })); - } else { - tracing::debug!( - "Failed to paste clipboard, unsupported MIME-type(s): {:?}", - content_formats.mime_types() - ); - } -} From f290a83b6880166d93a52efee9f9c89cedbd623c Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Tue, 20 Feb 2024 13:35:45 +0100 Subject: [PATCH 23/31] revert pen event for special dnd/paste mode --- crates/rnote-ui/src/canvas/input.rs | 99 ++++++++++++----------------- 1 file changed, 42 insertions(+), 57 deletions(-) diff --git a/crates/rnote-ui/src/canvas/input.rs b/crates/rnote-ui/src/canvas/input.rs index 3f060eff48..9b7f87dc86 100644 --- a/crates/rnote-ui/src/canvas/input.rs +++ b/crates/rnote-ui/src/canvas/input.rs @@ -67,45 +67,36 @@ pub(crate) fn handle_pointer_controller_event( let gdk_button = button_event.button(); let mut handle_shortcut_key = false; - // check this with a stylus : not working - // the intent would be to capture if a button on the stylus is pressed - // when the drag and drop occurs but nothing is captured when a drag and - // drop occurs from the pen on windows tracing::trace!( "canvas event ButtonPress - gdk_button: {gdk_button}, is_stylus: {is_stylus}" ); - if canvas.imp().dnd_status.get() { - handle_pen_event = false; - canvas.imp().dnd_respect_borders.set(true); + if is_stylus { + if gdk_button == gdk::BUTTON_PRIMARY + || gdk_button == gdk::BUTTON_SECONDARY + || gdk_button == gdk::BUTTON_MIDDLE + { + handle_pen_event = true; + handle_shortcut_key = true; + } } else { - if is_stylus { - if gdk_button == gdk::BUTTON_PRIMARY - || gdk_button == gdk::BUTTON_SECONDARY - || gdk_button == gdk::BUTTON_MIDDLE - { - handle_pen_event = true; - handle_shortcut_key = true; - } - } else { - #[allow(clippy::collapsible_else_if)] - if gdk_button == gdk::BUTTON_PRIMARY || gdk_button == gdk::BUTTON_SECONDARY { - handle_pen_event = true; - handle_shortcut_key = true; - pen_state = PenState::Down; - } + #[allow(clippy::collapsible_else_if)] + if gdk_button == gdk::BUTTON_PRIMARY || gdk_button == gdk::BUTTON_SECONDARY { + handle_pen_event = true; + handle_shortcut_key = true; + pen_state = PenState::Down; } + } - if handle_shortcut_key { - let shortcut_key = retrieve_button_shortcut_key(gdk_button, is_stylus); + if handle_shortcut_key { + let shortcut_key = retrieve_button_shortcut_key(gdk_button, is_stylus); - if let Some(shortcut_key) = shortcut_key { - let (ep, wf) = canvas - .engine_mut() - .handle_pressed_shortcut_key(shortcut_key, now); - widget_flags |= wf; - propagation = ep.into_glib(); - } + if let Some(shortcut_key) = shortcut_key { + let (ep, wf) = canvas + .engine_mut() + .handle_pressed_shortcut_key(shortcut_key, now); + widget_flags |= wf; + propagation = ep.into_glib(); } } } @@ -113,37 +104,31 @@ pub(crate) fn handle_pointer_controller_event( let button_event = event.downcast_ref::().unwrap(); let gdk_button = button_event.button(); - // check this with a stylus : not working, see below tracing::trace!( "canvas event ButtonRelease - gdk_button: {gdk_button}, is_stylus: {is_stylus}" ); - if canvas.imp().dnd_status.get() { - handle_pen_event = false; - canvas.imp().dnd_respect_borders.set(false); - } else { - if is_stylus { - if gdk_button == gdk::BUTTON_PRIMARY - || gdk_button == gdk::BUTTON_SECONDARY - || gdk_button == gdk::BUTTON_MIDDLE - { - handle_pen_event = true; - } - - // again, this is the method to detect proximity on stylus. - if gdk_button == gdk::BUTTON_PRIMARY { - pen_state = PenState::Up; - } else { - pen_state = PenState::Proximity; - } + if is_stylus { + if gdk_button == gdk::BUTTON_PRIMARY + || gdk_button == gdk::BUTTON_SECONDARY + || gdk_button == gdk::BUTTON_MIDDLE + { + handle_pen_event = true; + } + + // again, this is the method to detect proximity on stylus. + if gdk_button == gdk::BUTTON_PRIMARY { + pen_state = PenState::Up; } else { - #[allow(clippy::collapsible_else_if)] - if gdk_button == gdk::BUTTON_PRIMARY || gdk_button == gdk::BUTTON_SECONDARY { - pen_state = PenState::Up; - handle_pen_event = true; - } - }; - } + pen_state = PenState::Proximity; + } + } else { + #[allow(clippy::collapsible_else_if)] + if gdk_button == gdk::BUTTON_PRIMARY || gdk_button == gdk::BUTTON_SECONDARY { + pen_state = PenState::Up; + handle_pen_event = true; + } + }; } gdk::EventType::ProximityIn => { pen_state = PenState::Proximity; From 70db4a21106e96b2bd0ce281dfb7d63c018dc208 Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Tue, 20 Feb 2024 19:05:01 +0100 Subject: [PATCH 24/31] force pasted content to be inbounds for fixed layouts --- crates/rnote-engine/src/engine/import.rs | 6 +- crates/rnote-engine/src/strokes/resize.rs | 90 ++++++++++------------- crates/rnote-ui/src/appwindow/actions.rs | 32 ++++---- 3 files changed, 61 insertions(+), 67 deletions(-) diff --git a/crates/rnote-engine/src/engine/import.rs b/crates/rnote-engine/src/engine/import.rs index c08da58e47..2fc934d38d 100644 --- a/crates/rnote-engine/src/engine/import.rs +++ b/crates/rnote-engine/src/engine/import.rs @@ -246,7 +246,8 @@ impl Engine { width: width_page, height: height_page, isfixed_layout: is_fixed, - max_viewpoint: point_max, + max_viewpoint: Some(point_max), + restrain_to_viewport: true, respect_borders: respect_borders, }), ) @@ -288,7 +289,8 @@ impl Engine { width: width_page, height: height_page, isfixed_layout: is_fixed, - max_viewpoint: point_max, + max_viewpoint: Some(point_max), + restrain_to_viewport: true, respect_borders: respect_borders, }), ) diff --git a/crates/rnote-engine/src/strokes/resize.rs b/crates/rnote-engine/src/strokes/resize.rs index 830ee8af28..0c953b91d4 100644 --- a/crates/rnote-engine/src/strokes/resize.rs +++ b/crates/rnote-engine/src/strokes/resize.rs @@ -10,7 +10,7 @@ pub enum ImageSizeOption { RespectOriginalSize, /// Use the given size ImposeSize(na::Vector2), - /// Resize the image to canvas/page view + /// Resize the image with various constraints ResizeImage(Resize), } @@ -23,14 +23,30 @@ pub struct Resize { /// if the layout has a fixed size vertically pub isfixed_layout: bool, /// viewport - pub max_viewpoint: na::OPoint>, + pub max_viewpoint: Option>>, + /// resize to the viewport + pub restrain_to_viewport: bool, /// To force elements to not go over borders /// maybe enabling that to be on only when borders are active /// would be a better idea pub respect_borders: bool, } -/// Helper function to calculate ratios +/// helper functions for calculating resizing factors + +/// Calculate where the next border of the page is +/// based on the current `position` and the `size` of +/// the page length +/// +/// in conjunction with the the ratio min value, may +/// fail if the position is very close to a page border +fn helper_calculate_page_next_limit(position: &f64, size: &f64) -> f64 { + ((position / size).floor() + 1.0f64) * size +} + +/// Helper function to calculate ratios : min ratio for +/// the image to go from `current_position` to `current_size` +/// exactly fn helper_calculate_fit_ratio( max_position: &f64, current_position: &f64, @@ -58,81 +74,55 @@ pub fn calculate_resize_ratio( let current_width = initial_size_image.x; let current_height = initial_size_image.y; + let next_page_vertical_border = + helper_calculate_page_next_limit(&pos_left_top_canvas.index(0), &resize.width); + let next_page_horizontal_border = + helper_calculate_page_next_limit(&pos_left_top_canvas.index(1), &resize.height); + // compile all ratio in a vec let ratios = vec![ // check that we do not go out of the canvas view in the x direction helper_calculate_fit_ratio( - &resize.max_viewpoint.x, + &resize.max_viewpoint.unwrap_or(na::point![1.0, 1.0]).x, &pos_left_top_canvas.index(0), ¤t_width, ), // check that we do not go out of view in the y direction helper_calculate_fit_ratio( - &resize.max_viewpoint.y, + &resize.max_viewpoint.unwrap_or(na::point![1.0, 1.0]).y, &pos_left_top_canvas.index(1), ¤t_height, ), // check if we go out of the page on the right on fixed layout helper_calculate_fit_ratio(&resize.width, &pos_left_top_canvas.index(0), ¤t_width), // check if we have to respect borders - calculate_resize_ratio_respect_borders(&resize, &initial_size_image, &pos_left_top_canvas), - ]; - - // apply rules - let apply_ratios = vec![ - true, //canvas in the x direction - true, //canvas in the y direction - resize.isfixed_layout, //do not go over the page on the right for fixed layout - resize.respect_borders, //do not go over the page on the right for all layouts (slightly redundant) - ]; - - ratios - .iter() - .zip(apply_ratios) - .filter(|x| x.1) - .fold(1.0f64, |acc, x| acc.min(*x.0)) - .max(1e-15f64) //force the value to be positive as a zero would incurr crashes when applying the transforms -} - -/// calculate the ratio to not go over borders -pub fn calculate_resize_ratio_respect_borders( - resize: &Resize, - initial_size_image: &na::Vector2, - pos_left_top_canvas: &na::Vector2, -) -> f64 { - // closure to calculate the ceil - // We take the `floor (ratio + eps) + 1`` - // This allows for element that would fall exactly - // on a border to be on the next page and disallow - // too small ratios - let f_ratio = |position: &f64, size: &f64| -> f64 { - (((position / size) + 1e-8f64).floor() + 1.0f64) * size - }; - - let next_page_vertical_border = f_ratio(&pos_left_top_canvas.index(0), &resize.width); - let next_page_horizontal_border = f_ratio(&pos_left_top_canvas.index(1), &resize.height); - - let ratios = vec![ helper_calculate_fit_ratio( &next_page_vertical_border, &pos_left_top_canvas.index(0), &initial_size_image.x, - ), + ), // vertical border helper_calculate_fit_ratio( &next_page_horizontal_border, &pos_left_top_canvas.index(1), &initial_size_image.y, - ), + ), //horizontal border ]; - let rule_apply = vec![ - true, // vertical rule - true, // horizontal rule + let is_provided_viewport = resize.max_viewpoint.is_some(); + + // apply rules + let apply_ratios = vec![ + is_provided_viewport & resize.restrain_to_viewport, //canvas in the x direction + is_provided_viewport & resize.restrain_to_viewport, //canvas in the y direction + resize.isfixed_layout, //do not go over the page on the right for fixed layout + resize.respect_borders, //do not go over the page on the bottom for all layouts (slightly redundant) + resize.respect_borders, //do not go over the page on the right for all layouts (slightly redundant) ]; ratios .iter() - .zip(rule_apply) + .zip(apply_ratios) + .filter(|x| x.1) .fold(1.0f64, |acc, x| acc.min(*x.0)) - .max(1e-15f64) //force the final value to be positive + .max(1e-15f64) //force the value to be positive as a zero would incurr crashes when applying the transforms } diff --git a/crates/rnote-ui/src/appwindow/actions.rs b/crates/rnote-ui/src/appwindow/actions.rs index 51248fed19..fcff5eebea 100644 --- a/crates/rnote-ui/src/appwindow/actions.rs +++ b/crates/rnote-ui/src/appwindow/actions.rs @@ -817,6 +817,7 @@ impl RnAppWindow { } } + /// `respect_borders` : activate the special paste mode fn clipboard_paste(&self, target_pos: Option>, respect_borders: bool) { let canvas_wrapper = self.active_tab_wrapper(); let canvas = canvas_wrapper.canvas(); @@ -825,7 +826,7 @@ impl RnAppWindow { // Order matters here, we want to go from specific -> generic, mostly because `text/plain` is contained in other text based formats if content_formats.contain_mime_type("text/uri-list") { glib::spawn_future_local(clone!(@weak self as appwindow => async move { - tracing::debug!("Recognized clipboard content format: files list"); + tracing::trace!("Recognized clipboard content format: files list"); match appwindow.clipboard().read_text_future().await { Ok(Some(text)) => { @@ -856,7 +857,7 @@ impl RnAppWindow { })); } else if content_formats.contain_mime_type(StrokeContent::MIME_TYPE) { glib::spawn_future_local(clone!(@weak canvas, @weak self as appwindow => async move { - tracing::debug!("Recognized clipboard content format: {}", StrokeContent::MIME_TYPE); + tracing::trace!("Recognized clipboard content format: {}", StrokeContent::MIME_TYPE); match appwindow.clipboard().read_future(&[StrokeContent::MIME_TYPE], glib::source::Priority::DEFAULT).await { Ok((input_stream, _)) => { @@ -880,24 +881,25 @@ impl RnAppWindow { if !acc.is_empty() { match crate::utils::str_from_u8_nul_utf8(&acc) { Ok(json_string) => { - let resize_argument = match respect_borders { - false => ImageSizeOption::RespectOriginalSize, - true => { // get all info if resizing has to be done let width_page = canvas.engine_ref().document.format.width().clone(); let height_page = canvas.engine_ref().document.format.height().clone(); let is_fixed = canvas.engine_ref().document.layout.is_fixed_layout(); - let point_max: na::OPoint> = canvas.engine_ref().camera.viewport().maxs; - - ImageSizeOption::ResizeImage(Resize { + // let point_max: na::OPoint> = canvas.engine_ref().camera.viewport().maxs; + + // we choose here to + // - do not resize to the viewport in all cases (only reserved for paste of images) + // - resize to make the content fit on the page for fixed layouts + // - if the special method is activated, we will resize to make the + // content not go over any page borders + let resize_argument = ImageSizeOption::ResizeImage(Resize { width: width_page, height: height_page, isfixed_layout: is_fixed, - max_viewpoint: point_max, + max_viewpoint: None, + restrain_to_viewport: false, respect_borders: respect_borders, - }) - } - }; + }); if let Err(e) = canvas.insert_stroke_content(json_string.to_string(), resize_argument,target_pos).await { tracing::error!("Failed to insert stroke content while pasting as `{}`, Err: {e:?}", StrokeContent::MIME_TYPE); } @@ -916,7 +918,7 @@ impl RnAppWindow { })); } else if content_formats.contain_mime_type("image/svg+xml") { glib::spawn_future_local(clone!(@weak self as appwindow => async move { - tracing::debug!("Recognized clipboard content: svg image"); + tracing::trace!("Recognized clipboard content: svg image"); match appwindow.clipboard().read_future(&["image/svg+xml"], glib::source::Priority::DEFAULT).await { Ok((input_stream, _)) => { @@ -974,7 +976,7 @@ impl RnAppWindow { { glib::spawn_future_local( clone!(@weak canvas, @weak self as appwindow => async move { - tracing::debug!("Recognized clipboard content: bitmap image"); + tracing::trace!("Recognized clipboard content: bitmap image"); match appwindow.clipboard().read_texture_future().await { Ok(Some(texture)) => { @@ -998,7 +1000,7 @@ impl RnAppWindow { || content_formats.contain_mime_type("text/plain;charset=utf-8") { glib::spawn_future_local(clone!(@weak canvas, @weak self as appwindow => async move { - tracing::debug!("Recognized clipboard content: plain text"); + tracing::trace!("Recognized clipboard content: plain text"); match appwindow.clipboard().read_text_future().await { Ok(Some(text)) => { From 7bc854e4d18b012a9660dbcd6b14eb055f3184cf Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Sat, 24 Feb 2024 11:37:10 +0100 Subject: [PATCH 25/31] clippy fixes --- crates/rnote-engine/src/document/mod.rs | 6 +----- crates/rnote-engine/src/engine/import.rs | 12 +++++------ .../src/pens/selector/penevents.rs | 1 - .../rnote-engine/src/strokes/bitmapimage.rs | 4 +--- crates/rnote-engine/src/strokes/resize.rs | 20 ++++++++++--------- .../rnote-engine/src/strokes/vectorimage.rs | 6 +++--- crates/rnote-ui/data/ui/shortcuts.ui | 2 +- crates/rnote-ui/src/appwindow/actions.rs | 6 +++--- crates/rnote-ui/src/canvas/imexport.rs | 6 +++++- crates/rnote-ui/src/canvas/mod.rs | 2 +- 10 files changed, 32 insertions(+), 33 deletions(-) diff --git a/crates/rnote-engine/src/document/mod.rs b/crates/rnote-engine/src/document/mod.rs index 35632c054c..e0c12e2266 100644 --- a/crates/rnote-engine/src/document/mod.rs +++ b/crates/rnote-engine/src/document/mod.rs @@ -83,11 +83,7 @@ impl std::string::ToString for Layout { impl Layout { /// checks if the layout is constrained in the horizontal direction pub fn is_fixed_layout(&self) -> bool { - match self { - Layout::FixedSize => true, - Layout::ContinuousVertical => true, - _ => false, - } + matches!(self, Layout::FixedSize | Layout::ContinuousVertical) } } diff --git a/crates/rnote-engine/src/engine/import.rs b/crates/rnote-engine/src/engine/import.rs index 2fc934d38d..169db1684d 100644 --- a/crates/rnote-engine/src/engine/import.rs +++ b/crates/rnote-engine/src/engine/import.rs @@ -230,8 +230,8 @@ impl Engine { ) -> oneshot::Receiver> { let (oneshot_sender, oneshot_receiver) = oneshot::channel::>(); - let width_page = self.document.format.width().clone(); - let height_page = self.document.format.height().clone(); + let width_page = self.document.format.width(); + let height_page = self.document.format.height(); let is_fixed = self.document.layout.is_fixed_layout(); let point_max: na::OPoint> = self.camera.viewport().maxs; @@ -248,7 +248,7 @@ impl Engine { isfixed_layout: is_fixed, max_viewpoint: Some(point_max), restrain_to_viewport: true, - respect_borders: respect_borders, + respect_borders, }), ) }; @@ -275,8 +275,8 @@ impl Engine { let (oneshot_sender, oneshot_receiver) = oneshot::channel::>(); // we get these parameters to enable proper resizing of the windows - let width_page = self.document.format.width().clone(); - let height_page = self.document.format.height().clone(); + let width_page = self.document.format.width(); + let height_page = self.document.format.height(); let is_fixed = self.document.layout.is_fixed_layout(); let point_max: na::OPoint> = self.camera.viewport().maxs; @@ -291,7 +291,7 @@ impl Engine { isfixed_layout: is_fixed, max_viewpoint: Some(point_max), restrain_to_viewport: true, - respect_borders: respect_borders, + respect_borders, }), ) }; diff --git a/crates/rnote-engine/src/pens/selector/penevents.rs b/crates/rnote-engine/src/pens/selector/penevents.rs index c009942c16..1c5819a1a7 100644 --- a/crates/rnote-engine/src/pens/selector/penevents.rs +++ b/crates/rnote-engine/src/pens/selector/penevents.rs @@ -84,7 +84,6 @@ impl Selector { ) .pop(); - // ALL of the logic here to have the selection and resizing logic if (engine_view.pens_config.selector_config.style == SelectorStyle::Single || modifier_keys.contains(&ModifierKey::KeyboardShift)) && key_to_add diff --git a/crates/rnote-engine/src/strokes/bitmapimage.rs b/crates/rnote-engine/src/strokes/bitmapimage.rs index 180707b275..b09b2b4746 100644 --- a/crates/rnote-engine/src/strokes/bitmapimage.rs +++ b/crates/rnote-engine/src/strokes/bitmapimage.rs @@ -108,7 +108,6 @@ impl BitmapImage { let initial_size = na::vector![f64::from(image.pixel_width), f64::from(image.pixel_height)]; - // pattern match on ImageSizeOption let (size, resize_ratio) = match size { ImageSizeOption::RespectOriginalSize => (initial_size, 1.0f64), ImageSizeOption::ImposeSize(given_size) => (given_size, 1.0f64), @@ -119,13 +118,12 @@ impl BitmapImage { }; tracing::debug!("the resize ratio is {resize_ratio}"); - // general transform let mut transform = Transform::default(); transform.append_scale_mut(na::Vector2::new(resize_ratio, resize_ratio)); transform.append_translation_mut(pos + size * resize_ratio * 0.5); let rectangle = Rectangle { cuboid: p2d::shape::Cuboid::new(size * 0.5), - transform: transform, + transform, }; Ok(Self { image, rectangle }) } diff --git a/crates/rnote-engine/src/strokes/resize.rs b/crates/rnote-engine/src/strokes/resize.rs index 0c953b91d4..7f803ad612 100644 --- a/crates/rnote-engine/src/strokes/resize.rs +++ b/crates/rnote-engine/src/strokes/resize.rs @@ -74,36 +74,38 @@ pub fn calculate_resize_ratio( let current_width = initial_size_image.x; let current_height = initial_size_image.y; - let next_page_vertical_border = - helper_calculate_page_next_limit(&pos_left_top_canvas.index(0), &resize.width); + let pos_vertical = pos_left_top_canvas.index(0); + let pos_horizontal = pos_left_top_canvas.index(1); + + let next_page_vertical_border = helper_calculate_page_next_limit(pos_vertical, &resize.width); let next_page_horizontal_border = - helper_calculate_page_next_limit(&pos_left_top_canvas.index(1), &resize.height); + helper_calculate_page_next_limit(pos_horizontal, &resize.height); // compile all ratio in a vec - let ratios = vec![ + let ratios = [ // check that we do not go out of the canvas view in the x direction helper_calculate_fit_ratio( &resize.max_viewpoint.unwrap_or(na::point![1.0, 1.0]).x, - &pos_left_top_canvas.index(0), + pos_vertical, ¤t_width, ), // check that we do not go out of view in the y direction helper_calculate_fit_ratio( &resize.max_viewpoint.unwrap_or(na::point![1.0, 1.0]).y, - &pos_left_top_canvas.index(1), + pos_horizontal, ¤t_height, ), // check if we go out of the page on the right on fixed layout - helper_calculate_fit_ratio(&resize.width, &pos_left_top_canvas.index(0), ¤t_width), + helper_calculate_fit_ratio(&resize.width, pos_vertical, ¤t_width), // check if we have to respect borders helper_calculate_fit_ratio( &next_page_vertical_border, - &pos_left_top_canvas.index(0), + pos_horizontal, &initial_size_image.x, ), // vertical border helper_calculate_fit_ratio( &next_page_horizontal_border, - &pos_left_top_canvas.index(1), + pos_vertical, &initial_size_image.y, ), //horizontal border ]; diff --git a/crates/rnote-engine/src/strokes/vectorimage.rs b/crates/rnote-engine/src/strokes/vectorimage.rs index 04df845b38..971ef68779 100644 --- a/crates/rnote-engine/src/strokes/vectorimage.rs +++ b/crates/rnote-engine/src/strokes/vectorimage.rs @@ -170,7 +170,7 @@ impl VectorImage { transform.append_translation_mut(pos + intrinsic_size * 0.5); Rectangle { cuboid: p2d::shape::Cuboid::new(intrinsic_size * 0.5), - transform: transform, + transform, } } ImageSizeOption::ImposeSize(given_size) => { @@ -178,7 +178,7 @@ impl VectorImage { transform.append_translation_mut(pos + given_size * 0.5); Rectangle { cuboid: p2d::shape::Cuboid::new(given_size * 0.5), - transform: transform, + transform, } } ImageSizeOption::ResizeImage(resize_struct) => { @@ -187,7 +187,7 @@ impl VectorImage { transform.append_translation_mut(pos + intrinsic_size * resize_ratio * 0.5); Rectangle { cuboid: p2d::shape::Cuboid::new(intrinsic_size * resize_ratio * 0.5), - transform: transform, + transform, } } }; diff --git a/crates/rnote-ui/data/ui/shortcuts.ui b/crates/rnote-ui/data/ui/shortcuts.ui index 9dd12fcb9c..4df0c635eb 100644 --- a/crates/rnote-ui/data/ui/shortcuts.ui +++ b/crates/rnote-ui/data/ui/shortcuts.ui @@ -234,7 +234,7 @@ Paste Clipboard whilst respecting borders - <ctrl><shift>z + <ctrl><shift>v diff --git a/crates/rnote-ui/src/appwindow/actions.rs b/crates/rnote-ui/src/appwindow/actions.rs index fcff5eebea..f9bbc226a8 100644 --- a/crates/rnote-ui/src/appwindow/actions.rs +++ b/crates/rnote-ui/src/appwindow/actions.rs @@ -882,8 +882,8 @@ impl RnAppWindow { match crate::utils::str_from_u8_nul_utf8(&acc) { Ok(json_string) => { // get all info if resizing has to be done - let width_page = canvas.engine_ref().document.format.width().clone(); - let height_page = canvas.engine_ref().document.format.height().clone(); + let width_page = canvas.engine_ref().document.format.width(); + let height_page = canvas.engine_ref().document.format.height(); let is_fixed = canvas.engine_ref().document.layout.is_fixed_layout(); // let point_max: na::OPoint> = canvas.engine_ref().camera.viewport().maxs; @@ -898,7 +898,7 @@ impl RnAppWindow { isfixed_layout: is_fixed, max_viewpoint: None, restrain_to_viewport: false, - respect_borders: respect_borders, + respect_borders, }); if let Err(e) = canvas.insert_stroke_content(json_string.to_string(), resize_argument,target_pos).await { tracing::error!("Failed to insert stroke content while pasting as `{}`, Err: {e:?}", StrokeContent::MIME_TYPE); diff --git a/crates/rnote-ui/src/canvas/imexport.rs b/crates/rnote-ui/src/canvas/imexport.rs index ab69012eb2..46caed9b7f 100644 --- a/crates/rnote-ui/src/canvas/imexport.rs +++ b/crates/rnote-ui/src/canvas/imexport.rs @@ -180,7 +180,11 @@ impl RnCanvas { }); let content = oneshot_receiver.await??; - tracing::debug!("{:?} {:?}", content.bounds(), content.size()); //debug trace to see the size of the imported content + tracing::debug!( + "inserting stroke content : bounds\t {:?}\t content size:\t {:?}", + content.bounds(), + content.size() + ); let widget_flags = self .engine_mut() .insert_stroke_content(content, pos, resize); diff --git a/crates/rnote-ui/src/canvas/mod.rs b/crates/rnote-ui/src/canvas/mod.rs index b812f1f4fb..35cfa09119 100644 --- a/crates/rnote-ui/src/canvas/mod.rs +++ b/crates/rnote-ui/src/canvas/mod.rs @@ -157,7 +157,7 @@ mod imp { drawing_cursor: RefCell::new(drawing_cursor), drawing_cursor_icon_name: RefCell::new(drawing_cursor_icon_name), invisible_cursor: RefCell::new(invisible_cursor), - pointer_controller: pointer_controller, + pointer_controller, key_controller, key_controller_im_context, drop_target, From 6de68e28bf9428f37e0f4283b38bc88e237d33c8 Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Mon, 25 Mar 2024 21:12:30 +0100 Subject: [PATCH 26/31] cleaning --- crates/rnote-engine/src/engine/import.rs | 4 +- .../rnote-engine/src/strokes/bitmapimage.rs | 4 +- .../rnote-engine/src/strokes/vectorimage.rs | 3 +- crates/rnote-ui/src/app/mod.rs | 4 +- crates/rnote-ui/src/appwindow/actions.rs | 34 +++++------- crates/rnote-ui/src/canvas/imexport.rs | 2 +- crates/rnote-ui/src/canvas/input.rs | 54 ++++++------------- crates/rnote-ui/src/canvas/mod.rs | 26 +-------- crates/rnote-ui/src/workspacebrowser/mod.rs | 2 +- 9 files changed, 38 insertions(+), 95 deletions(-) diff --git a/crates/rnote-engine/src/engine/import.rs b/crates/rnote-engine/src/engine/import.rs index 86b9ec6125..118866fbc5 100644 --- a/crates/rnote-engine/src/engine/import.rs +++ b/crates/rnote-engine/src/engine/import.rs @@ -5,9 +5,7 @@ use crate::pens::Pen; use crate::pens::PenStyle; use crate::store::chrono_comp::StrokeLayer; use crate::store::StrokeKey; -use crate::strokes::resize::calculate_resize_ratio; -use crate::strokes::resize::ImageSizeOption; -use crate::strokes::Resize; +use crate::strokes::{resize::calculate_resize_ratio, resize::ImageSizeOption, Resize}; use crate::strokes::{BitmapImage, Stroke, VectorImage}; use crate::{CloneConfig, Engine, WidgetFlags}; use futures::channel::oneshot; diff --git a/crates/rnote-engine/src/strokes/bitmapimage.rs b/crates/rnote-engine/src/strokes/bitmapimage.rs index 720397af3d..a3bdc52102 100644 --- a/crates/rnote-engine/src/strokes/bitmapimage.rs +++ b/crates/rnote-engine/src/strokes/bitmapimage.rs @@ -1,10 +1,9 @@ // Imports -use super::resize::ImageSizeOption; +use super::resize::{calculate_resize_ratio, ImageSizeOption}; use super::{Content, Stroke}; use crate::document::Format; use crate::engine::import::{PdfImportPageSpacing, PdfImportPrefs}; use crate::render; -use crate::strokes::resize::calculate_resize_ratio; use crate::Drawable; use anyhow::Context; use kurbo::Shape; @@ -116,7 +115,6 @@ impl BitmapImage { calculate_resize_ratio(resize_struct, initial_size, pos), ), }; - tracing::debug!("the resize ratio is {resize_ratio}"); let mut transform = Transform::default(); transform.append_scale_mut(na::Vector2::new(resize_ratio, resize_ratio)); diff --git a/crates/rnote-engine/src/strokes/vectorimage.rs b/crates/rnote-engine/src/strokes/vectorimage.rs index 3a38b7e429..f396fb6feb 100644 --- a/crates/rnote-engine/src/strokes/vectorimage.rs +++ b/crates/rnote-engine/src/strokes/vectorimage.rs @@ -1,10 +1,9 @@ // Imports use super::content::GeneratedContentImages; -use super::resize::ImageSizeOption; +use super::resize::{calculate_resize_ratio, ImageSizeOption}; use super::{Content, Stroke}; use crate::document::Format; use crate::engine::import::{PdfImportPageSpacing, PdfImportPrefs}; -use crate::strokes::resize::calculate_resize_ratio; use crate::{render, Drawable}; use kurbo::Shape; use na; diff --git a/crates/rnote-ui/src/app/mod.rs b/crates/rnote-ui/src/app/mod.rs index 05d5db1d47..8fb1cfe05f 100644 --- a/crates/rnote-ui/src/app/mod.rs +++ b/crates/rnote-ui/src/app/mod.rs @@ -75,7 +75,7 @@ mod imp { { if let Some(input_file) = input_file { glib::spawn_future_local(clone!(@weak appwindow => async move { - appwindow.open_file_w_dialogs(input_file, None, true,false).await; + appwindow.open_file_w_dialogs(input_file, None, true, false).await; })); } } else { @@ -134,7 +134,7 @@ mod imp { // Loading in input file in the first tab, if Some if let Some(input_file) = input_file { glib::spawn_future_local(clone!(@weak appwindow => async move { - appwindow.open_file_w_dialogs(input_file, None, false,false).await; + appwindow.open_file_w_dialogs(input_file, None, false, false).await; })); } } diff --git a/crates/rnote-ui/src/appwindow/actions.rs b/crates/rnote-ui/src/appwindow/actions.rs index f9bbc226a8..74b826ead4 100644 --- a/crates/rnote-ui/src/appwindow/actions.rs +++ b/crates/rnote-ui/src/appwindow/actions.rs @@ -735,14 +735,13 @@ impl RnAppWindow { })); // Clipboard paste - // the logic has been moved to paste_content to make it possible to add a special paste method action_clipboard_respect_borders.connect_activate( clone!(@weak self as appwindow => move |_, _| { - appwindow.clipboard_paste(None,true); + appwindow.clipboard_paste(None, true); }), ); action_clipboard_paste.connect_activate(clone!(@weak self as appwindow => move |_, _| { - appwindow.clipboard_paste(None,false); + appwindow.clipboard_paste(None, false); })); action_clipboard_paste_contextmenu.connect_activate( @@ -757,7 +756,7 @@ impl RnAppWindow { .coords }); - appwindow.clipboard_paste(last_contextmenu_pos,false); + appwindow.clipboard_paste(last_contextmenu_pos, false); }), ); @@ -773,7 +772,7 @@ impl RnAppWindow { .coords }); - appwindow.clipboard_paste(last_contextmenu_pos,true); + appwindow.clipboard_paste(last_contextmenu_pos, true); }), ); } @@ -813,7 +812,7 @@ impl RnAppWindow { // shortcuts for devel build if config::PROFILE.to_lowercase().as_str() == "devel" { - app.set_accels_for_action("win.visual-debug", &["v"]); //clashes with the special paste method + app.set_accels_for_action("win.visual-debug", &["v"]); } } @@ -826,7 +825,7 @@ impl RnAppWindow { // Order matters here, we want to go from specific -> generic, mostly because `text/plain` is contained in other text based formats if content_formats.contain_mime_type("text/uri-list") { glib::spawn_future_local(clone!(@weak self as appwindow => async move { - tracing::trace!("Recognized clipboard content format: files list"); + tracing::debug!("Recognized clipboard content format: files list"); match appwindow.clipboard().read_text_future().await { Ok(Some(text)) => { @@ -845,7 +844,7 @@ impl RnAppWindow { }).collect::>(); for file_path in file_paths { - appwindow.open_file_w_dialogs(gio::File::for_path(&file_path), target_pos, false,respect_borders).await; + appwindow.open_file_w_dialogs(gio::File::for_path(&file_path), target_pos, false, respect_borders).await; } } Ok(None) => {} @@ -857,7 +856,7 @@ impl RnAppWindow { })); } else if content_formats.contain_mime_type(StrokeContent::MIME_TYPE) { glib::spawn_future_local(clone!(@weak canvas, @weak self as appwindow => async move { - tracing::trace!("Recognized clipboard content format: {}", StrokeContent::MIME_TYPE); + tracing::debug!("Recognized clipboard content format: {}", StrokeContent::MIME_TYPE); match appwindow.clipboard().read_future(&[StrokeContent::MIME_TYPE], glib::source::Priority::DEFAULT).await { Ok((input_stream, _)) => { @@ -881,17 +880,10 @@ impl RnAppWindow { if !acc.is_empty() { match crate::utils::str_from_u8_nul_utf8(&acc) { Ok(json_string) => { - // get all info if resizing has to be done let width_page = canvas.engine_ref().document.format.width(); let height_page = canvas.engine_ref().document.format.height(); let is_fixed = canvas.engine_ref().document.layout.is_fixed_layout(); - // let point_max: na::OPoint> = canvas.engine_ref().camera.viewport().maxs; - // we choose here to - // - do not resize to the viewport in all cases (only reserved for paste of images) - // - resize to make the content fit on the page for fixed layouts - // - if the special method is activated, we will resize to make the - // content not go over any page borders let resize_argument = ImageSizeOption::ResizeImage(Resize { width: width_page, height: height_page, @@ -918,7 +910,7 @@ impl RnAppWindow { })); } else if content_formats.contain_mime_type("image/svg+xml") { glib::spawn_future_local(clone!(@weak self as appwindow => async move { - tracing::trace!("Recognized clipboard content: svg image"); + tracing::debug!("Recognized clipboard content: svg image"); match appwindow.clipboard().read_future(&["image/svg+xml"], glib::source::Priority::DEFAULT).await { Ok((input_stream, _)) => { @@ -942,7 +934,7 @@ impl RnAppWindow { if !acc.is_empty() { match crate::utils::str_from_u8_nul_utf8(&acc) { Ok(text) => { - if let Err(e) = canvas.load_in_vectorimage_bytes(text.as_bytes().to_vec(), target_pos,respect_borders).await { + if let Err(e) = canvas.load_in_vectorimage_bytes(text.as_bytes().to_vec(), target_pos, respect_borders).await { tracing::error!( "Loading VectorImage bytes failed while pasting as Svg failed, Err: {e:?}" ); @@ -976,11 +968,11 @@ impl RnAppWindow { { glib::spawn_future_local( clone!(@weak canvas, @weak self as appwindow => async move { - tracing::trace!("Recognized clipboard content: bitmap image"); + tracing::debug!("Recognized clipboard content: bitmap image"); match appwindow.clipboard().read_texture_future().await { Ok(Some(texture)) => { - if let Err(e) = canvas.load_in_bitmapimage_bytes(texture.save_to_png_bytes().to_vec(), target_pos,respect_borders).await { + if let Err(e) = canvas.load_in_bitmapimage_bytes(texture.save_to_png_bytes().to_vec(), target_pos, respect_borders).await { tracing::error!( "Loading bitmap image bytes failed while pasting clipboard as {mime_type}, Err: {e:?}" ); @@ -1000,7 +992,7 @@ impl RnAppWindow { || content_formats.contain_mime_type("text/plain;charset=utf-8") { glib::spawn_future_local(clone!(@weak canvas, @weak self as appwindow => async move { - tracing::trace!("Recognized clipboard content: plain text"); + tracing::debug!("Recognized clipboard content: plain text"); match appwindow.clipboard().read_text_future().await { Ok(Some(text)) => { diff --git a/crates/rnote-ui/src/canvas/imexport.rs b/crates/rnote-ui/src/canvas/imexport.rs index ebd637e673..8cb7984fe8 100644 --- a/crates/rnote-ui/src/canvas/imexport.rs +++ b/crates/rnote-ui/src/canvas/imexport.rs @@ -171,7 +171,7 @@ impl RnCanvas { &self, json_string: String, resize: ImageSizeOption, - target_pos: Option>, // is this used ? To see if everything is okay ... + target_pos: Option>, ) -> anyhow::Result<()> { let (oneshot_sender, oneshot_receiver) = oneshot::channel::>(); diff --git a/crates/rnote-ui/src/canvas/input.rs b/crates/rnote-ui/src/canvas/input.rs index 9b7f87dc86..a0c96c9060 100644 --- a/crates/rnote-ui/src/canvas/input.rs +++ b/crates/rnote-ui/src/canvas/input.rs @@ -1,6 +1,5 @@ // Imports use super::RnCanvas; -use adw::glib::subclass::types::ObjectSubclassIsExt; use gtk4::{gdk, glib, graphene, prelude::*, Native}; use rnote_compose::penevent::{KeyboardKey, ModifierKey, PenEvent, PenState, ShortcutKey}; use rnote_compose::penpath::Element; @@ -236,51 +235,33 @@ pub(crate) fn handle_key_controller_key_pressed( let modifier_keys = retrieve_modifier_keys(gdk_modifiers); let shortcut_key = retrieve_keyboard_shortcut_key(gdk_key, gdk_modifiers); - match (keyboard_key, canvas.imp().dnd_status.get()) { - (KeyboardKey::ShiftLeft, true) | (KeyboardKey::ShiftRight, true) => { - //we only capture here if there is a drag and drop in progress - canvas.imp().dnd_respect_borders.set(true); - glib::Propagation::Stop - } - _ => { - let (propagation, widget_flags) = if let Some(shortcut_key) = shortcut_key { - canvas - .engine_mut() - .handle_pressed_shortcut_key(shortcut_key, now) - } else { - canvas.engine_mut().handle_pen_event( - PenEvent::KeyPressed { - keyboard_key, - modifier_keys, - }, - None, - now, - ) - }; + let (propagation, widget_flags) = if let Some(shortcut_key) = shortcut_key { + canvas + .engine_mut() + .handle_pressed_shortcut_key(shortcut_key, now) + } else { + canvas.engine_mut().handle_pen_event( + PenEvent::KeyPressed { + keyboard_key, + modifier_keys, + }, + None, + now, + ) + }; - canvas.emit_handle_widget_flags(widget_flags); - propagation.into_glib() - } - } + canvas.emit_handle_widget_flags(widget_flags); + propagation.into_glib() } pub(crate) fn handle_key_controller_key_released( - canvas: &RnCanvas, + _canvas: &RnCanvas, gdk_key: gdk::Key, gdk_modifiers: gdk::ModifierType, ) { tracing::trace!( "canvas event key released - gdk_key: {gdk_key:?}, gdk_modifiers: {gdk_modifiers:?}" ); - let keyboard_key = retrieve_keyboard_key(gdk_key); - - match (keyboard_key, canvas.imp().dnd_status.get()) { - (KeyboardKey::ShiftLeft, true) | (KeyboardKey::ShiftRight, true) => { - // we only remove the modifier if the drag and drop is in progress - canvas.imp().dnd_respect_borders.set(false); - } - _ => {} - } } pub(crate) fn handle_imcontext_text_commit(canvas: &RnCanvas, text: &str) { @@ -466,7 +447,6 @@ fn retrieve_pen_mode(event: &gdk::Event) -> Option { } } -/// correspond to KeyboardCtrlSpace pub(crate) fn retrieve_keyboard_shortcut_key( gdk_key: gdk::Key, modifier: gdk::ModifierType, diff --git a/crates/rnote-ui/src/canvas/mod.rs b/crates/rnote-ui/src/canvas/mod.rs index d5a01ab078..a7930529a1 100644 --- a/crates/rnote-ui/src/canvas/mod.rs +++ b/crates/rnote-ui/src/canvas/mod.rs @@ -71,10 +71,6 @@ mod imp { pub(crate) engine: RefCell, pub(crate) engine_task_handler_handle: RefCell>>, - // dnd status - pub(crate) dnd_status: Cell, - pub(crate) dnd_respect_borders: Cell, - pub(crate) output_file: RefCell>, pub(crate) output_file_watcher_task: RefCell>>, pub(crate) output_file_modified_toast_singleton: RefCell>, @@ -168,9 +164,6 @@ mod imp { engine: RefCell::new(engine), engine_task_handler_handle: RefCell::new(None), - dnd_status: Cell::new(false), - dnd_respect_borders: Cell::new(false), - output_file: RefCell::new(None), output_file_watcher_task: RefCell::new(None), // is automatically updated whenever the output file changes. @@ -1132,35 +1125,18 @@ impl RnCanvas { .build(); // Drop Target - // change the global state for dnd - self.imp().drop_target.connect_enter( - clone!(@weak self as canvas => @default-return gdk::DragAction::COPY, move |_,_,_| { - canvas.imp().dnd_status.set(true); - canvas.imp().dnd_respect_borders.set(false); - gdk::DragAction::COPY - }), - ); - - self.imp().drop_target.connect_leave( - clone!(@weak self as canvas => @default-return (), move |_| { - canvas.imp().dnd_status.set(false); // set the status to false - }), - ); let appwindow_drop_target = self.imp().drop_target.connect_drop( clone!(@weak self as canvas, @weak appwindow => @default-return false, move |_, value, x, y| { let pos = (canvas.engine_ref().camera.transform().inverse() * na::point![x,y]).coords; let mut accept_drop = false; - // should we respect borders ? - let respect_border = canvas.imp().dnd_respect_borders.get(); - if value.is::() { // In some scenarios, get() can fail with `UnexpectedNone` even though is() returned true, e.g. when dealing with trashed files. match value.get::() { Ok(file) => { glib::spawn_future_local(clone!(@weak appwindow => async move { - appwindow.open_file_w_dialogs(file, Some(pos), true,respect_border).await; + appwindow.open_file_w_dialogs(file, Some(pos), true, false).await; })); accept_drop = true; }, diff --git a/crates/rnote-ui/src/workspacebrowser/mod.rs b/crates/rnote-ui/src/workspacebrowser/mod.rs index cc033e92c0..f4d8779ebe 100644 --- a/crates/rnote-ui/src/workspacebrowser/mod.rs +++ b/crates/rnote-ui/src/workspacebrowser/mod.rs @@ -298,7 +298,7 @@ impl RnWorkspaceBrowser { let file_info = listview.model().unwrap().item(position).unwrap().downcast::().unwrap(); if let Some(input_file) = file_info.attribute_object("standard::file") { glib::spawn_future_local(clone!(@weak appwindow => async move { - appwindow.open_file_w_dialogs(input_file.downcast::().unwrap(), None, true,false).await; + appwindow.open_file_w_dialogs(input_file.downcast::().unwrap(), None, true, false).await; })); }; folders_filter.changed(FilterChange::Different); From c3eca5e45345aa9c4b0efd9c4c0f3cfa287a8389 Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Fri, 29 Mar 2024 09:10:34 +0100 Subject: [PATCH 27/31] more cleaning --- crates/rnote-engine/src/engine/import.rs | 45 +++++++++-------------- crates/rnote-engine/src/strokes/resize.rs | 39 ++++++-------------- crates/rnote-ui/src/appwindow/actions.rs | 12 ++---- crates/rnote-ui/src/canvas/imexport.rs | 6 --- 4 files changed, 34 insertions(+), 68 deletions(-) diff --git a/crates/rnote-engine/src/engine/import.rs b/crates/rnote-engine/src/engine/import.rs index 118866fbc5..0c492bb60f 100644 --- a/crates/rnote-engine/src/engine/import.rs +++ b/crates/rnote-engine/src/engine/import.rs @@ -235,11 +235,14 @@ impl Engine { ) -> oneshot::Receiver> { let (oneshot_sender, oneshot_receiver) = oneshot::channel::>(); - let width_page = self.document.format.width(); - let height_page = self.document.format.height(); - let is_fixed = self.document.layout.is_fixed_layout(); - let point_max: na::OPoint> = self.camera.viewport().maxs; - + let resize_struct = Resize { + width: self.document.format.width(), + height: self.document.format.height(), + isfixed_layout: self.document.layout.is_fixed_layout(), + max_viewpoint: Some(self.camera.viewport().maxs), + restrain_to_viewport: true, + respect_borders, + }; rayon::spawn(move || { let result = || -> anyhow::Result { let svg_str = String::from_utf8(bytes)?; @@ -247,14 +250,7 @@ impl Engine { VectorImage::from_svg_str( &svg_str, pos, - ImageSizeOption::ResizeImage(Resize { - width: width_page, - height: height_page, - isfixed_layout: is_fixed, - max_viewpoint: Some(point_max), - restrain_to_viewport: true, - respect_borders, - }), + ImageSizeOption::ResizeImage(resize_struct), ) }; @@ -279,25 +275,20 @@ impl Engine { ) -> oneshot::Receiver> { let (oneshot_sender, oneshot_receiver) = oneshot::channel::>(); - // we get these parameters to enable proper resizing of the windows - let width_page = self.document.format.width(); - let height_page = self.document.format.height(); - let is_fixed = self.document.layout.is_fixed_layout(); - let point_max: na::OPoint> = self.camera.viewport().maxs; - + let resize_struct = Resize { + width: self.document.format.width(), + height: self.document.format.height(), + isfixed_layout: self.document.layout.is_fixed_layout(), + max_viewpoint: Some(self.camera.viewport().maxs), + restrain_to_viewport: true, + respect_borders, + }; rayon::spawn(move || { let result = || -> anyhow::Result { BitmapImage::from_image_bytes( &bytes, pos, - ImageSizeOption::ResizeImage(Resize { - width: width_page, - height: height_page, - isfixed_layout: is_fixed, - max_viewpoint: Some(point_max), - restrain_to_viewport: true, - respect_borders, - }), + ImageSizeOption::ResizeImage(resize_struct), ) }; diff --git a/crates/rnote-engine/src/strokes/resize.rs b/crates/rnote-engine/src/strokes/resize.rs index 7f803ad612..ba0aa3c957 100644 --- a/crates/rnote-engine/src/strokes/resize.rs +++ b/crates/rnote-engine/src/strokes/resize.rs @@ -71,43 +71,28 @@ pub fn calculate_resize_ratio( initial_size_image: na::Vector2, pos_left_top_canvas: na::Vector2, ) -> f64 { - let current_width = initial_size_image.x; - let current_height = initial_size_image.y; - - let pos_vertical = pos_left_top_canvas.index(0); - let pos_horizontal = pos_left_top_canvas.index(1); - - let next_page_vertical_border = helper_calculate_page_next_limit(pos_vertical, &resize.width); - let next_page_horizontal_border = - helper_calculate_page_next_limit(pos_horizontal, &resize.height); + let next_page_x = helper_calculate_page_next_limit(&pos_left_top_canvas.x, &resize.width); + let next_page_y = helper_calculate_page_next_limit(&pos_left_top_canvas.y, &resize.height); // compile all ratio in a vec let ratios = [ // check that we do not go out of the canvas view in the x direction helper_calculate_fit_ratio( &resize.max_viewpoint.unwrap_or(na::point![1.0, 1.0]).x, - pos_vertical, - ¤t_width, + &pos_left_top_canvas.x, + &initial_size_image.x, ), // check that we do not go out of view in the y direction helper_calculate_fit_ratio( &resize.max_viewpoint.unwrap_or(na::point![1.0, 1.0]).y, - pos_horizontal, - ¤t_height, + &pos_left_top_canvas.y, + &initial_size_image.y, ), // check if we go out of the page on the right on fixed layout - helper_calculate_fit_ratio(&resize.width, pos_vertical, ¤t_width), + helper_calculate_fit_ratio(&resize.width, &pos_left_top_canvas.x, &initial_size_image.x), // check if we have to respect borders - helper_calculate_fit_ratio( - &next_page_vertical_border, - pos_horizontal, - &initial_size_image.x, - ), // vertical border - helper_calculate_fit_ratio( - &next_page_horizontal_border, - pos_vertical, - &initial_size_image.y, - ), //horizontal border + helper_calculate_fit_ratio(&next_page_y, &pos_left_top_canvas.y, &initial_size_image.y), // vertical border (cut in the y direction) + helper_calculate_fit_ratio(&next_page_x, &pos_left_top_canvas.x, &initial_size_image.x), // horizontal border (cut in the x direction) ]; let is_provided_viewport = resize.max_viewpoint.is_some(); @@ -117,8 +102,8 @@ pub fn calculate_resize_ratio( is_provided_viewport & resize.restrain_to_viewport, //canvas in the x direction is_provided_viewport & resize.restrain_to_viewport, //canvas in the y direction resize.isfixed_layout, //do not go over the page on the right for fixed layout - resize.respect_borders, //do not go over the page on the bottom for all layouts (slightly redundant) - resize.respect_borders, //do not go over the page on the right for all layouts (slightly redundant) + resize.respect_borders, //do not go over the page on the bottom for all layouts + resize.respect_borders, //do not go over the page on the right for all layouts ]; ratios @@ -126,5 +111,5 @@ pub fn calculate_resize_ratio( .zip(apply_ratios) .filter(|x| x.1) .fold(1.0f64, |acc, x| acc.min(*x.0)) - .max(1e-15f64) //force the value to be positive as a zero would incurr crashes when applying the transforms + .max(1e-15f64) //force the value to be positive as a zero would make transforms crash } diff --git a/crates/rnote-ui/src/appwindow/actions.rs b/crates/rnote-ui/src/appwindow/actions.rs index 74b826ead4..6ffe8c9b6a 100644 --- a/crates/rnote-ui/src/appwindow/actions.rs +++ b/crates/rnote-ui/src/appwindow/actions.rs @@ -880,19 +880,15 @@ impl RnAppWindow { if !acc.is_empty() { match crate::utils::str_from_u8_nul_utf8(&acc) { Ok(json_string) => { - let width_page = canvas.engine_ref().document.format.width(); - let height_page = canvas.engine_ref().document.format.height(); - let is_fixed = canvas.engine_ref().document.layout.is_fixed_layout(); - let resize_argument = ImageSizeOption::ResizeImage(Resize { - width: width_page, - height: height_page, - isfixed_layout: is_fixed, + width: canvas.engine_ref().document.format.width(), + height: canvas.engine_ref().document.format.height(), + isfixed_layout: canvas.engine_ref().document.layout.is_fixed_layout(), max_viewpoint: None, restrain_to_viewport: false, respect_borders, }); - if let Err(e) = canvas.insert_stroke_content(json_string.to_string(), resize_argument,target_pos).await { + if let Err(e) = canvas.insert_stroke_content(json_string.to_string(), resize_argument, target_pos).await { tracing::error!("Failed to insert stroke content while pasting as `{}`, Err: {e:?}", StrokeContent::MIME_TYPE); } } diff --git a/crates/rnote-ui/src/canvas/imexport.rs b/crates/rnote-ui/src/canvas/imexport.rs index 8cb7984fe8..b12708433e 100644 --- a/crates/rnote-ui/src/canvas/imexport.rs +++ b/crates/rnote-ui/src/canvas/imexport.rs @@ -188,12 +188,6 @@ impl RnCanvas { } }); let content = oneshot_receiver.await??; - - tracing::debug!( - "inserting stroke content : bounds\t {:?}\t content size:\t {:?}", - content.bounds(), - content.size() - ); let widget_flags = self .engine_mut() .insert_stroke_content(content, pos, resize); From 4b482bf6dc4d09204ce703d912bbc4153d63e159 Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Sat, 20 Apr 2024 12:27:08 +0200 Subject: [PATCH 28/31] renaming --- crates/rnote-engine/src/document/mod.rs | 2 +- crates/rnote-engine/src/engine/import.rs | 4 ++-- crates/rnote-engine/src/strokes/bitmapimage.rs | 4 ++-- crates/rnote-engine/src/strokes/resize.rs | 8 ++++---- crates/rnote-engine/src/strokes/vectorimage.rs | 5 ++--- crates/rnote-ui/src/appwindow/actions.rs | 2 +- crates/rnote-ui/src/appwindow/mod.rs | 10 +++++----- crates/rnote-ui/src/canvas/imexport.rs | 4 ++-- 8 files changed, 19 insertions(+), 20 deletions(-) diff --git a/crates/rnote-engine/src/document/mod.rs b/crates/rnote-engine/src/document/mod.rs index bc71b18152..a2e01da3dd 100644 --- a/crates/rnote-engine/src/document/mod.rs +++ b/crates/rnote-engine/src/document/mod.rs @@ -82,7 +82,7 @@ impl std::string::ToString for Layout { impl Layout { /// checks if the layout is constrained in the horizontal direction - pub fn is_fixed_layout(&self) -> bool { + pub fn is_fixed_width(&self) -> bool { matches!(self, Layout::FixedSize | Layout::ContinuousVertical) } } diff --git a/crates/rnote-engine/src/engine/import.rs b/crates/rnote-engine/src/engine/import.rs index 0c492bb60f..deabac069a 100644 --- a/crates/rnote-engine/src/engine/import.rs +++ b/crates/rnote-engine/src/engine/import.rs @@ -238,7 +238,7 @@ impl Engine { let resize_struct = Resize { width: self.document.format.width(), height: self.document.format.height(), - isfixed_layout: self.document.layout.is_fixed_layout(), + layout_fixed_width: self.document.layout.is_fixed_width(), max_viewpoint: Some(self.camera.viewport().maxs), restrain_to_viewport: true, respect_borders, @@ -278,7 +278,7 @@ impl Engine { let resize_struct = Resize { width: self.document.format.width(), height: self.document.format.height(), - isfixed_layout: self.document.layout.is_fixed_layout(), + layout_fixed_width: self.document.layout.is_fixed_width(), max_viewpoint: Some(self.camera.viewport().maxs), restrain_to_viewport: true, respect_borders, diff --git a/crates/rnote-engine/src/strokes/bitmapimage.rs b/crates/rnote-engine/src/strokes/bitmapimage.rs index a3bdc52102..b59d7dab98 100644 --- a/crates/rnote-engine/src/strokes/bitmapimage.rs +++ b/crates/rnote-engine/src/strokes/bitmapimage.rs @@ -101,13 +101,13 @@ impl BitmapImage { pub fn from_image_bytes( bytes: &[u8], pos: na::Vector2, - size: ImageSizeOption, + size_option: ImageSizeOption, ) -> Result { let image = render::Image::try_from_encoded_bytes(bytes)?; let initial_size = na::vector![f64::from(image.pixel_width), f64::from(image.pixel_height)]; - let (size, resize_ratio) = match size { + let (size, resize_ratio) = match size_option { ImageSizeOption::RespectOriginalSize => (initial_size, 1.0f64), ImageSizeOption::ImposeSize(given_size) => (given_size, 1.0f64), ImageSizeOption::ResizeImage(resize_struct) => ( diff --git a/crates/rnote-engine/src/strokes/resize.rs b/crates/rnote-engine/src/strokes/resize.rs index ba0aa3c957..060170b37b 100644 --- a/crates/rnote-engine/src/strokes/resize.rs +++ b/crates/rnote-engine/src/strokes/resize.rs @@ -21,7 +21,7 @@ pub struct Resize { /// height of a page pub height: f64, /// if the layout has a fixed size vertically - pub isfixed_layout: bool, + pub layout_fixed_width: bool, /// viewport pub max_viewpoint: Option>>, /// resize to the viewport @@ -101,9 +101,9 @@ pub fn calculate_resize_ratio( let apply_ratios = vec![ is_provided_viewport & resize.restrain_to_viewport, //canvas in the x direction is_provided_viewport & resize.restrain_to_viewport, //canvas in the y direction - resize.isfixed_layout, //do not go over the page on the right for fixed layout - resize.respect_borders, //do not go over the page on the bottom for all layouts - resize.respect_borders, //do not go over the page on the right for all layouts + resize.layout_fixed_width, //do not go over the page on the right for fixed layout + resize.respect_borders, //do not go over the page on the bottom for all layouts + resize.respect_borders, //do not go over the page on the right for all layouts ]; ratios diff --git a/crates/rnote-engine/src/strokes/vectorimage.rs b/crates/rnote-engine/src/strokes/vectorimage.rs index f396fb6feb..5b5ed49ad0 100644 --- a/crates/rnote-engine/src/strokes/vectorimage.rs +++ b/crates/rnote-engine/src/strokes/vectorimage.rs @@ -6,7 +6,6 @@ use crate::document::Format; use crate::engine::import::{PdfImportPageSpacing, PdfImportPrefs}; use crate::{render, Drawable}; use kurbo::Shape; -use na; use p2d::bounding_volume::Aabb; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rnote_compose::color; @@ -139,7 +138,7 @@ impl VectorImage { pub fn from_svg_str( svg_data: &str, pos: na::Vector2, - size: ImageSizeOption, + size_option: ImageSizeOption, ) -> Result { const COORDINATES_PREC: u8 = 3; const TRANSFORMS_PREC: u8 = 4; @@ -163,7 +162,7 @@ impl VectorImage { let svg_data = svg_tree.to_string(&xml_options); let mut transform = Transform::default(); - let rectangle = match size { + let rectangle = match size_option { ImageSizeOption::RespectOriginalSize => { // Size not given : use the intrisic size transform.append_translation_mut(pos + intrinsic_size * 0.5); diff --git a/crates/rnote-ui/src/appwindow/actions.rs b/crates/rnote-ui/src/appwindow/actions.rs index 6ffe8c9b6a..7f804296cd 100644 --- a/crates/rnote-ui/src/appwindow/actions.rs +++ b/crates/rnote-ui/src/appwindow/actions.rs @@ -883,7 +883,7 @@ impl RnAppWindow { let resize_argument = ImageSizeOption::ResizeImage(Resize { width: canvas.engine_ref().document.format.width(), height: canvas.engine_ref().document.format.height(), - isfixed_layout: canvas.engine_ref().document.layout.is_fixed_layout(), + layout_fixed_width: canvas.engine_ref().document.layout.is_fixed_width(), max_viewpoint: None, restrain_to_viewport: false, respect_borders, diff --git a/crates/rnote-ui/src/appwindow/mod.rs b/crates/rnote-ui/src/appwindow/mod.rs index 655409e418..cc2790eee0 100644 --- a/crates/rnote-ui/src/appwindow/mod.rs +++ b/crates/rnote-ui/src/appwindow/mod.rs @@ -469,11 +469,11 @@ impl RnAppWindow { input_file: gio::File, target_pos: Option>, rnote_file_new_tab: bool, - respect_border: bool, + respect_borders: bool, ) { self.overlays().progressbar_start_pulsing(); match self - .try_open_file(input_file, target_pos, rnote_file_new_tab, respect_border) + .try_open_file(input_file, target_pos, rnote_file_new_tab, respect_borders) .await { Ok(true) => { @@ -500,7 +500,7 @@ impl RnAppWindow { input_file: gio::File, target_pos: Option>, rnote_file_new_tab: bool, - respect_border: bool, + respect_borders: bool, ) -> anyhow::Result { let file_imported = match FileType::lookup_file_type(&input_file) { FileType::RnoteFile => { @@ -543,7 +543,7 @@ impl RnAppWindow { let canvas = self.active_tab_wrapper().canvas(); let (bytes, _) = input_file.load_bytes_future().await?; canvas - .load_in_vectorimage_bytes(bytes.to_vec(), target_pos, respect_border) + .load_in_vectorimage_bytes(bytes.to_vec(), target_pos, respect_borders) .await?; true } @@ -551,7 +551,7 @@ impl RnAppWindow { let canvas = self.active_tab_wrapper().canvas(); let (bytes, _) = input_file.load_bytes_future().await?; canvas - .load_in_bitmapimage_bytes(bytes.to_vec(), target_pos, respect_border) + .load_in_bitmapimage_bytes(bytes.to_vec(), target_pos, respect_borders) .await?; true } diff --git a/crates/rnote-ui/src/canvas/imexport.rs b/crates/rnote-ui/src/canvas/imexport.rs index b12708433e..aa26ad0a8e 100644 --- a/crates/rnote-ui/src/canvas/imexport.rs +++ b/crates/rnote-ui/src/canvas/imexport.rs @@ -170,7 +170,7 @@ impl RnCanvas { pub(crate) async fn insert_stroke_content( &self, json_string: String, - resize: ImageSizeOption, + resize_option: ImageSizeOption, target_pos: Option>, ) -> anyhow::Result<()> { let (oneshot_sender, oneshot_receiver) = @@ -190,7 +190,7 @@ impl RnCanvas { let content = oneshot_receiver.await??; let widget_flags = self .engine_mut() - .insert_stroke_content(content, pos, resize); + .insert_stroke_content(content, pos, resize_option); self.emit_handle_widget_flags(widget_flags); Ok(()) From 0ae41c0444e2ed9ce2638ecb16ecb20c85cd65e5 Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Mon, 22 Apr 2024 19:36:36 +0200 Subject: [PATCH 29/31] add a `respect border` toggle --- Cargo.lock | 516 +++++++++++------------ crates/rnote-ui/data/app.gschema.xml.in | 4 + crates/rnote-ui/data/ui/canvasmenu.ui | 5 + crates/rnote-ui/src/appwindow/actions.rs | 3 + crates/rnote-ui/src/appwindow/imp.rs | 11 + crates/rnote-ui/src/canvaswrapper.rs | 27 ++ 6 files changed, 304 insertions(+), 262 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 07c7748de2..ed38fc4914 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,18 +31,18 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alsa" @@ -51,7 +51,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37fe60779335388a88c01ac6c3be40304d1e349de3ada3b15f7808bb90fa9dce" dependencies = [ "alsa-sys", - "bitflags 2.4.2", + "bitflags 2.5.0", "libc", ] @@ -130,9 +130,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.80" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "approx" @@ -173,24 +173,23 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" +checksum = "136d4d23bcc79e27423727b36823d86233aad06dfea531837b038394d11e9928" dependencies = [ "concurrent-queue", - "event-listener 5.2.0", - "event-listener-strategy 0.5.0", + "event-listener 5.3.0", + "event-listener-strategy 0.5.1", "futures-core", "pin-project-lite", ] [[package]] name = "async-executor" -version = "1.8.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" +checksum = "b10202063978b3351199d68f8b22c4e47e4b1b822f8d43fd862d5ea8c006b29a" dependencies = [ - "async-lock 3.3.0", "async-task", "concurrent-queue", "fastrand", @@ -204,18 +203,18 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc19683171f287921f2405677dd2ed2549c3b3bda697a563ebc3a121ace2aba1" dependencies = [ - "async-lock 3.3.0", + "async-lock", "blocking", "futures-lite", ] [[package]] name = "async-io" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f97ab0c5b00a7cdbe5a371b9a782ee7be1316095885c8a4ea1daf490eb0ef65" +checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" dependencies = [ - "async-lock 3.3.0", + "async-lock", "cfg-if", "concurrent-queue", "futures-io", @@ -228,15 +227,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener 2.5.3", -] - [[package]] name = "async-lock" version = "3.3.0" @@ -261,30 +251,32 @@ dependencies = [ [[package]] name = "async-process" -version = "2.1.0" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "451e3cf68011bd56771c79db04a9e333095ab6349f7e47592b788e9b98720cc8" +checksum = "a53fc6301894e04a92cb2584fedde80cb25ba8e02d9dc39d4a87d036e22f397d" dependencies = [ "async-channel", "async-io", - "async-lock 3.3.0", + "async-lock", "async-signal", + "async-task", "blocking", "cfg-if", - "event-listener 5.2.0", + "event-listener 5.3.0", "futures-lite", "rustix", + "tracing", "windows-sys 0.52.0", ] [[package]] name = "async-signal" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +checksum = "afe66191c335039c7bb78f99dc7520b0cbb166b3a1cb33a03f53d8a1c6f2afda" dependencies = [ "async-io", - "async-lock 2.8.0", + "async-lock", "atomic-waker", "cfg-if", "futures-core", @@ -292,7 +284,7 @@ dependencies = [ "rustix", "signal-hook-registry", "slab", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -320,9 +312,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "autocxx" @@ -356,7 +348,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.52", + "syn 2.0.60", "which", ] @@ -369,7 +361,7 @@ dependencies = [ "autocxx-engine", "env_logger", "indexmap 1.9.3", - "syn 2.0.52", + "syn 2.0.60", ] [[package]] @@ -396,7 +388,7 @@ dependencies = [ "rustversion", "serde_json", "strum_macros", - "syn 2.0.52", + "syn 2.0.60", "tempfile", "thiserror", "version_check", @@ -412,7 +404,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.60", ] [[package]] @@ -429,15 +421,15 @@ dependencies = [ "quote", "serde", "serde_json", - "syn 2.0.52", + "syn 2.0.60", "thiserror", ] [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -469,7 +461,7 @@ version = "0.69.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "cexpr", "clang-sys", "itertools 0.12.1", @@ -480,7 +472,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.52", + "syn 2.0.60", ] [[package]] @@ -497,9 +489,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "block" @@ -514,7 +506,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" dependencies = [ "async-channel", - "async-lock 3.3.0", + "async-lock", "async-task", "fastrand", "futures-io", @@ -525,15 +517,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.4" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.14.3" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" [[package]] name = "byteorder" @@ -543,17 +535,17 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "cairo-rs" -version = "0.19.2" +version = "0.19.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2650f66005301bd33cc486dec076e1293c4cecf768bc7ba9bf5d2b1be339b99c" +checksum = "b2ac2a4d0e69036cf0062976f6efcba1aaee3e448594e6514bb2ddf87acce562" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "cairo-sys-rs", "glib", "libc", @@ -579,12 +571,13 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.90" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" +checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" dependencies = [ "jobserver", "libc", + "once_cell", ] [[package]] @@ -604,9 +597,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.15.7" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa50868b64a9a6fda9d593ce778849ea8715cd2a3d2cc17ffdb4a2f2f2f1961d" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" dependencies = [ "smallvec", "target-lexicon", @@ -620,16 +613,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.35" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -645,9 +638,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.2" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b230ab84b0ffdf890d5a10abdbc8b83ae1c4918275daea1ab8801f71536b2651" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", "clap_derive", @@ -662,19 +655,19 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.0", + "strsim 0.11.1", ] [[package]] name = "clap_derive" -version = "4.5.0" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.60", ] [[package]] @@ -716,9 +709,9 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "combine" -version = "4.6.6" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ "bytes", "memchr", @@ -864,14 +857,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.52", + "syn 2.0.60", ] [[package]] name = "cxx" -version = "1.0.119" +version = "1.0.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "635179be18797d7e10edb9cd06c859580237750c7351f39ed9b298bfc17544ad" +checksum = "21db378d04296a84d8b7d047c36bb3954f0b46529db725d7e62fb02f9ba53ccc" dependencies = [ "cc", "cxxbridge-flags", @@ -881,31 +874,31 @@ dependencies = [ [[package]] name = "cxx-gen" -version = "0.7.119" +version = "0.7.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5797d553b95704a6a49394acfdb93e2332b8aaa146713a1e8ebe362e86d9fa68" +checksum = "383ecb9f96a536a1c7a2a61c5786f583da84f9240da149d78d005a4413c9a71e" dependencies = [ "codespan-reporting", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.60", ] [[package]] name = "cxxbridge-flags" -version = "1.0.119" +version = "1.0.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a87ff7342ffaa54b7c61618e0ce2bbcf827eba6d55b923b83d82551acbbecfe5" +checksum = "be8dcadd2e2fb4a501e1d9e93d6e88e6ea494306d8272069c92d5a9edf8855c0" [[package]] name = "cxxbridge-macro" -version = "1.0.119" +version = "1.0.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70b5b86cf65fa0626d85720619d80b288013477a91a0389fa8bc716bf4903ad1" +checksum = "ad08a837629ad949b73d032c637653d069e909cffe4ee7870b02301939ce39cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.60", ] [[package]] @@ -1022,9 +1015,9 @@ dependencies = [ [[package]] name = "downcast-rs" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "dtoa" @@ -1053,9 +1046,9 @@ dependencies = [ [[package]] name = "either" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "encode_unicode" @@ -1065,9 +1058,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] @@ -1110,12 +1103,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - [[package]] name = "event-listener" version = "4.0.3" @@ -1129,9 +1116,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b5fb89194fa3cad959b833185b3063ba881dbfc7030680b314250779fb4cc91" +checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" dependencies = [ "concurrent-queue", "parking", @@ -1150,11 +1137,11 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291" +checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3" dependencies = [ - "event-listener 5.2.0", + "event-listener 5.3.0", "pin-project-lite", ] @@ -1188,9 +1175,9 @@ checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" [[package]] name = "fastrand" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" [[package]] name = "fdeflate" @@ -1379,9 +1366,9 @@ checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-lite" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" dependencies = [ "fastrand", "futures-core", @@ -1398,7 +1385,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.60", ] [[package]] @@ -1537,9 +1524,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "libc", @@ -1584,9 +1571,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "gio" -version = "0.19.2" +version = "0.19.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eae10b27b6dd27e22ed0d812c6387deba295e6fc004a8b379e459b663b05a02" +checksum = "3f91a0518c2ec539f099d3f945ab2d6a83ec372a9ef40a21906343b191182845" dependencies = [ "futures-channel", "futures-core", @@ -1615,11 +1602,11 @@ dependencies = [ [[package]] name = "glib" -version = "0.19.2" +version = "0.19.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab9e86540b5d8402e905ad4ce7d6aa544092131ab564f3102175af176b90a053" +checksum = "ae1407b2ce171e654720be10d57d4054d3ff2f10a13d5b37e6819b41439832f7" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "futures-channel", "futures-core", "futures-executor", @@ -1646,15 +1633,15 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.19.2" +version = "0.19.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f5897ca27a83e4cdc7b4666850bade0a2e73e17689aabafcc9acddad9d823b8" +checksum = "d8bba315e8ce8aa59631545358450f4962557e89b5f7db7442e7153b47037f71" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.60", ] [[package]] @@ -1794,9 +1781,9 @@ dependencies = [ [[package]] name = "half" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" dependencies = [ "cfg-if", "crunchy", @@ -1843,6 +1830,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -1961,9 +1954,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.5" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -2105,9 +2098,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jni" @@ -2133,9 +2126,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.28" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" dependencies = [ "libc", ] @@ -2266,7 +2259,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -2277,9 +2270,9 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "librsvg" -version = "2.58.0-beta.1" +version = "2.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59d441368ddd551d54ef8ebcf515cf8a22604856a66da21828fa1d57651a9f71" +checksum = "de005d9589235493d0e2b62d055c0d8e368db4fb33e746dafc9400fb67b9c817" dependencies = [ "cairo-rs", "cast", @@ -2420,9 +2413,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memmap2" @@ -2435,9 +2428,9 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] @@ -2471,7 +2464,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.60", ] [[package]] @@ -2513,9 +2506,9 @@ dependencies = [ [[package]] name = "nalgebra" -version = "0.32.4" +version = "0.32.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4541eb06dce09c0241ebbaab7102f0a01a0c8994afed2e5d0d66775016e25ac2" +checksum = "3ea4908d4f23254adda3daa60ffef0f1ac7b8c3e9a864cf3cc154b251908a2ef" dependencies = [ "approx", "matrixmultiply", @@ -2545,7 +2538,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "jni-sys", "log", "ndk-sys", @@ -2570,15 +2563,9 @@ dependencies = [ [[package]] name = "new_debug_unreachable" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" - -[[package]] -name = "no-std-compat" -version = "0.4.1" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "nom" @@ -2596,7 +2583,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "crossbeam-channel", "filetime", "fsevent-sys", @@ -2651,7 +2638,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.60", ] [[package]] @@ -2712,7 +2699,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.60", ] [[package]] @@ -2723,12 +2710,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "numeric-sort" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5f2c4adf593c928171160e2632096353bb0306d3c8447143861374d5e678e37" -dependencies = [ - "no-std-compat", -] +checksum = "5180ed575d2ae6100dc63813e3810aba273c291608cf019aa58f76784ecc331b" [[package]] name = "objc" @@ -2840,14 +2824,14 @@ checksum = "e8890702dbec0bad9116041ae586f84805b13eecd1d8b1df27c29998a9969d6d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.60", ] [[package]] name = "pango" -version = "0.19.2" +version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7809e8af4df8d024a066106b72ca6bc7253a484ae3867041a96103ef8a13188d" +checksum = "b1264d13deb823cc652f26cfe59afb1ec4b9db2a5bd27c41b738c879cc1bfaa1" dependencies = [ "gio", "glib", @@ -2924,9 +2908,9 @@ dependencies = [ [[package]] name = "parry2d-f64" -version = "0.13.6" +version = "0.13.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "542444b3ef557470c352ca13ba34a3d5e558b34265339320b8fb560bc82c427e" +checksum = "5565e2f28bad470868b5e5c146fd0e2716345b67d8b9f27e0fc147a1dd4790b5" dependencies = [ "approx", "arrayvec", @@ -3051,7 +3035,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.60", ] [[package]] @@ -3102,9 +3086,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -3154,12 +3138,13 @@ dependencies = [ [[package]] name = "polling" -version = "3.5.0" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24f040dee2588b4963afb4e420540439d126f73fdacf4a9c486a96d840bac3c9" +checksum = "e0c976a60b2d7e99d6f229e414670a9b85d13ac305cc6d1e9c134de58c5aaaf6" dependencies = [ "cfg-if", "concurrent-queue", + "hermit-abi 0.3.9", "pin-project-lite", "rustix", "tracing", @@ -3213,12 +3198,12 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "prettyplease" -version = "0.2.16" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +checksum = "5ac2cf0f2e4f42b49f5ffd07dae8d746508ef7526c13940e5f524012ae6c6550" dependencies = [ "proc-macro2", - "syn 2.0.52", + "syn 2.0.60", ] [[package]] @@ -3256,9 +3241,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] @@ -3274,9 +3259,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -3338,9 +3323,9 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "rayon" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -3373,14 +3358,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.3" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", "regex-automata 0.4.6", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", ] [[package]] @@ -3400,7 +3385,7 @@ checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", ] [[package]] @@ -3411,9 +3396,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rgb" @@ -3659,11 +3644,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.31" +version = "0.38.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", @@ -3672,9 +3657,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" [[package]] name = "rustybuzz" @@ -3682,7 +3667,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0ae5692c5beaad6a9e22830deeed7874eae8a4e3ba4076fb48e12c56856222c" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "bytemuck", "smallvec", "ttf-parser", @@ -3728,7 +3713,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4eb30575f3638fc8f6815f448d50cb1a2e255b0897985c8c59f4d37b72a07b06" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "cssparser", "derive_more", "fxhash", @@ -3752,29 +3737,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.60", ] [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "itoa", "ryu", @@ -3822,9 +3807,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -3884,9 +3869,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "smawk" @@ -3904,7 +3889,7 @@ dependencies = [ "async-executor", "async-fs", "async-io", - "async-lock 3.3.0", + "async-lock", "async-net", "async-process", "blocking", @@ -3981,9 +3966,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "strsim" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum_macros" @@ -3991,7 +3976,7 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", @@ -4144,9 +4129,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.52" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -4155,14 +4140,14 @@ dependencies = [ [[package]] name = "system-deps" -version = "6.2.0" +version = "6.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2d580ff6a20c55dfb86be5f9c238f67835d0e81cbdea8bf5680e0897320331" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" dependencies = [ "cfg-expr", - "heck", + "heck 0.5.0", "pkg-config", - "toml 0.8.10", + "toml 0.8.12", "version-compare", ] @@ -4174,9 +4159,9 @@ checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" [[package]] name = "temp-dir" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd16aa9ffe15fe021c6ee3766772132c6e98dfa395a167e16864f61a9cfb71d6" +checksum = "1f227968ec00f0e5322f9b8173c7a0cbcff6181a0a5b28e9892491c286277231" [[package]] name = "tempfile" @@ -4233,22 +4218,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.57" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.60", ] [[package]] @@ -4312,14 +4297,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.10" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.6", + "toml_edit 0.22.12", ] [[package]] @@ -4337,7 +4322,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", @@ -4350,22 +4335,22 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.2.6", "toml_datetime", "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.22.6" +version = "0.22.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.5", + "winnow 0.6.6", ] [[package]] @@ -4387,7 +4372,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.60", ] [[package]] @@ -4619,9 +4604,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "version-compare" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" [[package]] name = "version_check" @@ -4666,7 +4651,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.60", "wasm-bindgen-shared", ] @@ -4700,7 +4685,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.60", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4741,9 +4726,9 @@ dependencies = [ [[package]] name = "wide" -version = "0.7.15" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89beec544f246e679fc25490e3f8e08003bc4bf612068f325120dad4cea02c1c" +checksum = "81a1851a719f11d1d2fea40e15c72f6c00de8c142d7ac47c1441cc7e4d0d5bc6" dependencies = [ "bytemuck", "safe_arch", @@ -4787,7 +4772,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" dependencies = [ "windows-core 0.54.0", - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -4796,7 +4781,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -4806,16 +4791,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" dependencies = [ "windows-result", - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] name = "windows-result" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd19df78e5168dfb0aedc343d1d1b8d422ab2db6756d2dc3fef75035402a3f64" +checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -4842,7 +4827,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -4877,17 +4862,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -4904,9 +4890,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -4922,9 +4908,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -4940,9 +4926,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -4958,9 +4950,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -4976,9 +4968,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -4994,9 +4986,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -5012,9 +5004,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" @@ -5027,9 +5019,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" dependencies = [ "memchr", ] @@ -5084,7 +5076,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.60", ] [[package]] diff --git a/crates/rnote-ui/data/app.gschema.xml.in b/crates/rnote-ui/data/app.gschema.xml.in index 1eaade44fa..040a243e5e 100644 --- a/crates/rnote-ui/data/app.gschema.xml.in +++ b/crates/rnote-ui/data/app.gschema.xml.in @@ -141,6 +141,10 @@ false block pinch to zoom + + false + respect borders when pasting + false enable drawing with touch input diff --git a/crates/rnote-ui/data/ui/canvasmenu.ui b/crates/rnote-ui/data/ui/canvasmenu.ui index ff6544ecb8..5490729b45 100644 --- a/crates/rnote-ui/data/ui/canvasmenu.ui +++ b/crates/rnote-ui/data/ui/canvasmenu.ui @@ -60,6 +60,11 @@ win.block-pinch-zoom + + Respect Borders when Pasting + + win.respect-borders + diff --git a/crates/rnote-ui/src/appwindow/actions.rs b/crates/rnote-ui/src/appwindow/actions.rs index 7f804296cd..3f27606fdc 100644 --- a/crates/rnote-ui/src/appwindow/actions.rs +++ b/crates/rnote-ui/src/appwindow/actions.rs @@ -82,6 +82,9 @@ impl RnAppWindow { let action_block_pinch_zoom = gio::PropertyAction::new("block-pinch-zoom", self, "block-pinch-zoom"); self.add_action(&action_block_pinch_zoom); + let action_respect_borders = + gio::PropertyAction::new("respect-borders", self, "respect-borders"); + self.add_action(&action_respect_borders); let action_pen_style = gio::SimpleAction::new_stateful( "pen-style", Some(&String::static_variant_type()), diff --git a/crates/rnote-ui/src/appwindow/imp.rs b/crates/rnote-ui/src/appwindow/imp.rs index 06cec379e6..0eb74f8bc6 100644 --- a/crates/rnote-ui/src/appwindow/imp.rs +++ b/crates/rnote-ui/src/appwindow/imp.rs @@ -21,6 +21,7 @@ pub(crate) struct RnAppWindow { pub(crate) autosave_interval_secs: Cell, pub(crate) righthanded: Cell, pub(crate) block_pinch_zoom: Cell, + pub(crate) respect_borders: Cell, pub(crate) touch_drawing: Cell, pub(crate) focus_mode: Cell, @@ -47,6 +48,7 @@ impl Default for RnAppWindow { autosave_interval_secs: Cell::new(super::RnAppWindow::AUTOSAVE_INTERVAL_DEFAULT), righthanded: Cell::new(true), block_pinch_zoom: Cell::new(false), + respect_borders: Cell::new(false), touch_drawing: Cell::new(false), focus_mode: Cell::new(false), @@ -127,6 +129,9 @@ impl ObjectImpl for RnAppWindow { glib::ParamSpecBoolean::builder("touch-drawing") .default_value(false) .build(), + glib::ParamSpecBoolean::builder("respect-borders") + .default_value(false) + .build(), glib::ParamSpecBoolean::builder("focus-mode") .default_value(false) .build(), @@ -141,6 +146,7 @@ impl ObjectImpl for RnAppWindow { "autosave-interval-secs" => self.autosave_interval_secs.get().to_value(), "righthanded" => self.righthanded.get().to_value(), "block-pinch-zoom" => self.block_pinch_zoom.get().to_value(), + "respect-borders" => self.respect_borders.get().to_value(), "touch-drawing" => self.touch_drawing.get().to_value(), "focus-mode" => self.focus_mode.get().to_value(), _ => unimplemented!(), @@ -188,6 +194,11 @@ impl ObjectImpl for RnAppWindow { value.get().expect("The value needs to be of type `bool`"); self.block_pinch_zoom.replace(block_pinch_zoom); } + "respect-borders" => { + let respect_borders: bool = + value.get().expect("The value needs to be of type `bool`"); + self.respect_borders.replace(respect_borders); + } "touch-drawing" => { let touch_drawing: bool = value.get().expect("The value needs to be of type `bool`"); diff --git a/crates/rnote-ui/src/canvaswrapper.rs b/crates/rnote-ui/src/canvaswrapper.rs index 8920475c9e..ca6fe200d1 100644 --- a/crates/rnote-ui/src/canvaswrapper.rs +++ b/crates/rnote-ui/src/canvaswrapper.rs @@ -20,6 +20,7 @@ struct Connections { appwindow_show_scrollbars_bind: Option, appwindow_inertial_scrolling_bind: Option, appwindow_righthanded_bind: Option, + appwindow_respect_borders_bind: Option, } mod imp { @@ -33,6 +34,7 @@ mod imp { pub(crate) show_scrollbars: Cell, pub(crate) block_pinch_zoom: Cell, pub(crate) inertial_scrolling: Cell, + pub(crate) respect_borders: Cell, pub(crate) pointer_pos: Cell>>, pub(crate) last_contextmenu_pos: Cell>>, @@ -124,6 +126,7 @@ mod imp { show_scrollbars: Cell::new(false), block_pinch_zoom: Cell::new(false), inertial_scrolling: Cell::new(true), + respect_borders: Cell::new(false), pointer_pos: Cell::new(None), last_contextmenu_pos: Cell::new(None), @@ -232,6 +235,9 @@ mod imp { glib::ParamSpecBoolean::builder("inertial-scrolling") .default_value(true) .build(), + glib::ParamSpecBoolean::builder("respect-borders") + .default_value(false) + .build(), ] }); PROPERTIES.as_ref() @@ -242,6 +248,7 @@ mod imp { "show-scrollbars" => self.show_scrollbars.get().to_value(), "block-pinch-zoom" => self.block_pinch_zoom.get().to_value(), "inertial-scrolling" => self.inertial_scrolling.get().to_value(), + "respect-borders" => self.respect_borders.get().to_value(), _ => unimplemented!(), } } @@ -272,6 +279,12 @@ mod imp { self.inertial_scrolling.replace(inertial_scrolling); self.canvas_kinetic_scrolling_update(); } + "respect-borders" => { + let respect_borders = value + .get::() + .expect("The value needs to be of type bool"); + self.respect_borders.replace(respect_borders); + } _ => unimplemented!(), } } @@ -705,6 +718,11 @@ impl RnCanvasWrapper { .sync_create() .build(); + let appwindow_respect_borders_bind = appwindow + .bind_property("respect-borders", self, "respect_borders") + .sync_create() + .build(); + let appwindow_show_scrollbars_bind = appwindow .sidebar() .settings_panel() @@ -758,6 +776,12 @@ impl RnCanvasWrapper { { old.unbind(); } + if let Some(old) = connections + .appwindow_respect_borders_bind + .replace(appwindow_respect_borders_bind) + { + old.unbind(); + } } /// This disconnects all connections with references to external objects, @@ -780,6 +804,9 @@ impl RnCanvasWrapper { if let Some(old) = connections.appwindow_righthanded_bind.take() { old.unbind(); } + if let Some(old) = connections.appwindow_respect_borders_bind.take() { + old.unbind(); + } } /// When the widget is the child of a tab page, we want to connect the title, icons, .. From 9ba9dc25a225d76893ec1778e4c8dbc5ff0b0801 Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Mon, 22 Apr 2024 20:54:46 +0200 Subject: [PATCH 30/31] only use the `respect-borders` value from the windows --- crates/rnote-engine/src/strokes/resize.rs | 2 -- crates/rnote-ui/data/ui/appmenu.ui | 4 --- crates/rnote-ui/data/ui/contextmenu.ui | 4 --- crates/rnote-ui/data/ui/shortcuts.ui | 6 ----- crates/rnote-ui/src/appwindow/actions.rs | 30 +++-------------------- crates/rnote-ui/src/canvaswrapper.rs | 5 ++++ 6 files changed, 8 insertions(+), 43 deletions(-) diff --git a/crates/rnote-engine/src/strokes/resize.rs b/crates/rnote-engine/src/strokes/resize.rs index 060170b37b..9914045780 100644 --- a/crates/rnote-engine/src/strokes/resize.rs +++ b/crates/rnote-engine/src/strokes/resize.rs @@ -1,5 +1,3 @@ -use na; - /// Enum that lists the different options for sizing the image /// /// Either respect the original image size (in pixel or dimensions) diff --git a/crates/rnote-ui/data/ui/appmenu.ui b/crates/rnote-ui/data/ui/appmenu.ui index d3b4376509..2f478eeee3 100644 --- a/crates/rnote-ui/data/ui/appmenu.ui +++ b/crates/rnote-ui/data/ui/appmenu.ui @@ -108,10 +108,6 @@ _Paste win.clipboard-paste - - _Paste whilst respecting borders - win.clipboard-paste-respect-borders - _Export… diff --git a/crates/rnote-ui/data/ui/contextmenu.ui b/crates/rnote-ui/data/ui/contextmenu.ui index 8734c69339..c49ab3b14d 100644 --- a/crates/rnote-ui/data/ui/contextmenu.ui +++ b/crates/rnote-ui/data/ui/contextmenu.ui @@ -18,10 +18,6 @@ _Paste win.clipboard-paste-contextmenu - - _Paste whilst respecting borders - win.clipboard-paste-contextmenu-special - diff --git a/crates/rnote-ui/data/ui/shortcuts.ui b/crates/rnote-ui/data/ui/shortcuts.ui index 4df0c635eb..2e9b227875 100644 --- a/crates/rnote-ui/data/ui/shortcuts.ui +++ b/crates/rnote-ui/data/ui/shortcuts.ui @@ -231,12 +231,6 @@ <ctrl>v - - - Paste Clipboard whilst respecting borders - <ctrl><shift>v - - Duplicate Selection diff --git a/crates/rnote-ui/src/appwindow/actions.rs b/crates/rnote-ui/src/appwindow/actions.rs index 3f27606fdc..7e010bb516 100644 --- a/crates/rnote-ui/src/appwindow/actions.rs +++ b/crates/rnote-ui/src/appwindow/actions.rs @@ -1,5 +1,6 @@ // Imports use crate::{config, dialogs, RnAppWindow, RnCanvas}; +use adw::glib::property::PropertyGet; use gettextrs::gettext; use gtk4::graphene; use gtk4::{ @@ -155,9 +156,6 @@ impl RnAppWindow { let action_clipboard_paste_contextmenu = gio::SimpleAction::new("clipboard-paste-contextmenu", None); self.add_action(&action_clipboard_paste_contextmenu); - let action_clipboard_paste_contextmenu_special = - gio::SimpleAction::new("clipboard-paste-contextmenu-special", None); - self.add_action(&action_clipboard_paste_contextmenu_special); let action_active_tab_move_left = gio::SimpleAction::new("active-tab-move-left", None); self.add_action(&action_active_tab_move_left); let action_active_tab_move_right = gio::SimpleAction::new("active-tab-move-right", None); @@ -738,13 +736,8 @@ impl RnAppWindow { })); // Clipboard paste - action_clipboard_respect_borders.connect_activate( - clone!(@weak self as appwindow => move |_, _| { - appwindow.clipboard_paste(None, true); - }), - ); action_clipboard_paste.connect_activate(clone!(@weak self as appwindow => move |_, _| { - appwindow.clipboard_paste(None, false); + appwindow.clipboard_paste(None, appwindow.active_tab_wrapper().respect_borders()); })); action_clipboard_paste_contextmenu.connect_activate( @@ -762,22 +755,6 @@ impl RnAppWindow { appwindow.clipboard_paste(last_contextmenu_pos, false); }), ); - - action_clipboard_paste_contextmenu_special.connect_activate( - clone!(@weak self as appwindow => move |_, _| { - let canvas_wrapper = appwindow.active_tab_wrapper(); - let canvas = canvas_wrapper.canvas(); - - let last_contextmenu_pos = canvas_wrapper.last_contextmenu_pos().map(|vec2| { - let p = graphene::Point::new(vec2.x as f32, vec2.y as f32); - (canvas.engine_ref().camera.transform().inverse() - * na::point![p.x() as f64, p.y() as f64]) - .coords - }); - - appwindow.clipboard_paste(last_contextmenu_pos, true); - }), - ); } pub(crate) fn setup_action_accels(&self) { @@ -805,7 +782,6 @@ impl RnAppWindow { app.set_accels_for_action("win.clipboard-copy", &["c"]); app.set_accels_for_action("win.clipboard-cut", &["x"]); app.set_accels_for_action("win.clipboard-paste", &["v"]); - app.set_accels_for_action("win.clipboard-paste-respect-borders", &["v"]); app.set_accels_for_action("win.pen-style::brush", &["1"]); app.set_accels_for_action("win.pen-style::shaper", &["2"]); app.set_accels_for_action("win.pen-style::typewriter", &["3"]); @@ -815,7 +791,7 @@ impl RnAppWindow { // shortcuts for devel build if config::PROFILE.to_lowercase().as_str() == "devel" { - app.set_accels_for_action("win.visual-debug", &["v"]); + app.set_accels_for_action("win.visual-debug", &["v"]); } } diff --git a/crates/rnote-ui/src/canvaswrapper.rs b/crates/rnote-ui/src/canvaswrapper.rs index ca6fe200d1..3775a686dd 100644 --- a/crates/rnote-ui/src/canvaswrapper.rs +++ b/crates/rnote-ui/src/canvaswrapper.rs @@ -674,6 +674,11 @@ impl RnCanvasWrapper { self.property::("block-pinch-zoom") } + #[allow(unused)] + pub(crate) fn respect_borders(&self) -> bool { + self.property::("respect-borders") + } + #[allow(unused)] pub(crate) fn set_block_pinch_zoom(&self, block_pinch_zoom: bool) { self.set_property("block-pinch-zoom", block_pinch_zoom); From 0c1b4d7eac91f8f028cb3ebee760324774c8f032 Mon Sep 17 00:00:00 2001 From: Doublonmousse <115779707+Doublonmousse@users.noreply.github.com> Date: Mon, 22 Apr 2024 23:45:43 +0200 Subject: [PATCH 31/31] tweaking `respect_borders` conditions (wip) --- crates/rnote-ui/src/appwindow/actions.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/rnote-ui/src/appwindow/actions.rs b/crates/rnote-ui/src/appwindow/actions.rs index 7e010bb516..1bc729140f 100644 --- a/crates/rnote-ui/src/appwindow/actions.rs +++ b/crates/rnote-ui/src/appwindow/actions.rs @@ -1,6 +1,5 @@ // Imports use crate::{config, dialogs, RnAppWindow, RnCanvas}; -use adw::glib::property::PropertyGet; use gettextrs::gettext; use gtk4::graphene; use gtk4::{ @@ -752,7 +751,7 @@ impl RnAppWindow { .coords }); - appwindow.clipboard_paste(last_contextmenu_pos, false); + appwindow.clipboard_paste(last_contextmenu_pos, appwindow.active_tab_wrapper().respect_borders()); }), ); } @@ -795,7 +794,6 @@ impl RnAppWindow { } } - /// `respect_borders` : activate the special paste mode fn clipboard_paste(&self, target_pos: Option>, respect_borders: bool) { let canvas_wrapper = self.active_tab_wrapper(); let canvas = canvas_wrapper.canvas();