diff --git a/Cargo.lock b/Cargo.lock index 204b74a576..f774d921d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,6 +29,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "aligned-vec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" + [[package]] name = "allocator-api2" version = "0.2.18" @@ -135,6 +141,23 @@ dependencies = [ "num-traits", ] +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "arrayref" version = "0.3.7" @@ -294,6 +317,29 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "av1-grain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2" +dependencies = [ + "arrayvec", +] + [[package]] name = "base64" version = "0.22.1" @@ -338,6 +384,12 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +[[package]] +name = "bitstream-io" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c12d1856e42f0d817a835fe55853957c85c8c8a470114029143d3f12671446e" + [[package]] name = "block" version = "0.1.6" @@ -392,6 +444,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.6.0" @@ -686,7 +744,7 @@ dependencies = [ "cssparser-macros", "dtoa-short", "itoa", - "phf 0.11.2", + "phf 0.10.1", "smallvec", ] @@ -1041,14 +1099,14 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a595cb550439a117696039dfc69830492058211b771a2a165379f2a1a53d84d" dependencies = [ - "roxmltree", + "roxmltree 0.19.0", ] [[package]] name = "fontdb" -version = "0.16.2" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0299020c3ef3f60f526a4f64ab4a3d4ce116b1acbf24cdd22da0068e5d81dc3" +checksum = "e32eac81c1135c1df01d4e6d4233c47ba11f6a6d07f33e0bba09d18797077770" dependencies = [ "fontconfig-parser", "log", @@ -1676,12 +1734,51 @@ dependencies = [ "tiff", ] +[[package]] +name = "image" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd54d660e773627692c524beaad361aca785a4f9f5730ce91f42aabe5bce3d11" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "image-webp", + "num-traits", + "png", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d730b085583c4d789dfd07fdcf185be59501666a90c97c40162b37e4fdad272d" +dependencies = [ + "byteorder-lite", + "thiserror", +] + [[package]] name = "imagesize" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" +[[package]] +name = "imgref" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" + [[package]] name = "indexmap" version = "2.2.6" @@ -1742,6 +1839,36 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.0" @@ -1766,6 +1893,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -1924,13 +2060,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] -name = "libdbus-sys" -version = "0.2.5" +name = "libfuzzer-sys" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" dependencies = [ + "arbitrary", "cc", - "pkg-config", + "once_cell", ] [[package]] @@ -1940,7 +2077,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets 0.52.5", + "windows-targets 0.48.5", ] [[package]] @@ -1963,7 +2100,7 @@ dependencies = [ "float-cmp", "gio", "glib", - "image", + "image 0.24.9", "itertools 0.12.1", "language-tags", "libc", @@ -2020,6 +2157,15 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + [[package]] name = "mac" version = "0.1.1" @@ -2083,6 +2229,16 @@ dependencies = [ "rawpointer", ] +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + [[package]] name = "memchr" version = "2.7.2" @@ -2217,6 +2373,7 @@ dependencies = [ "windows-sys 0.52.0", ] + [[package]] name = "notify" version = "6.1.1" @@ -2260,6 +2417,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-complex" version = "0.4.6" @@ -2296,6 +2463,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ + "num-bigint", "num-integer", "num-traits", ] @@ -2535,15 +2703,16 @@ dependencies = [ [[package]] name = "parry2d-f64" -version = "0.13.8" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91945e2f204070ca52b591324c1c7f4cdf112f013f3e7b89805b4e01e07143a3" +checksum = "416b765a3db5f3e219fcc878d649ea6dc6b93490694be846c39c7398074f90d1" dependencies = [ "approx", "arrayvec", "bitflags 1.3.2", "downcast-rs", "either", + "log", "nalgebra", "num-derive", "num-traits", @@ -2591,7 +2760,9 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" dependencies = [ + "phf_macros 0.10.0", "phf_shared 0.10.0", + "proc-macro-hack", ] [[package]] @@ -2600,7 +2771,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ - "phf_macros", + "phf_macros 0.11.2", "phf_shared 0.11.2", ] @@ -2634,6 +2805,20 @@ dependencies = [ "rand", ] +[[package]] +name = "phf_macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "phf_macros" version = "0.11.2" @@ -2814,6 +2999,12 @@ dependencies = [ "toml_edit 0.21.1", ] +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + [[package]] name = "proc-macro2" version = "1.0.85" @@ -2823,6 +3014,25 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "profiling" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" +dependencies = [ + "quote", + "syn 2.0.66", +] + [[package]] name = "qoi" version = "0.4.1" @@ -2832,6 +3042,12 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quote" version = "1.0.36" @@ -2890,6 +3106,56 @@ dependencies = [ "rand_core", ] +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools 0.12.1", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "once_cell", + "paste", + "profiling", + "rand", + "rand_chacha", + "simd_helpers", + "system-deps", + "thiserror", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc13288f5ab39e6d7c9d501759712e6969fcc9734220846fc9ed26cae2cc4234" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + [[package]] name = "rawpointer" version = "0.2.1" @@ -3008,8 +3274,8 @@ dependencies = [ "glib-build-tools", "gtk4", "ijson", - "image", - "itertools 0.12.1", + "image 0.25.1", + "itertools 0.13.0", "kurbo 0.10.4", "libadwaita", "nalgebra", @@ -3110,8 +3376,8 @@ dependencies = [ "glib", "gtk4", "ijson", - "image", - "itertools 0.12.1", + "image 0.25.1", + "itertools 0.13.0", "kurbo 0.10.4", "librsvg", "nalgebra", @@ -3131,7 +3397,7 @@ dependencies = [ "rodio", "rough_piet", "roughr", - "roxmltree", + "roxmltree 0.20.0", "rstar", "semver", "serde", @@ -3153,12 +3419,13 @@ checksum = "cbf4a6aa5f6d6888f39e980649f3ad6b666acdce1d78e95b8a2cb076e687ae30" [[package]] name = "rodio" -version = "0.17.3" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b1bb7b48ee48471f55da122c0044fcc7600cfcc85db88240b89cb832935e611" +checksum = "d1fceb9d127d515af1586d8d0cc601e1245bdb0af38e75c865a156290184f5b3" dependencies = [ "cpal", "symphonia", + "thiserror", ] [[package]] @@ -3196,6 +3463,12 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + [[package]] name = "rstar" version = "0.12.0" @@ -3237,9 +3510,9 @@ dependencies = [ [[package]] name = "rustybuzz" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88117946aa1bfb53c2ae0643ceac6506337f44887f8c9fbfb43587b1cc52ba49" +checksum = "7730060ad401b0d1807c904ea56735288af101430aa0d2ab8358b789f5f37002" dependencies = [ "bitflags 2.5.0", "bytemuck", @@ -3407,6 +3680,15 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + [[package]] name = "simplecss" version = "0.2.1" @@ -3546,9 +3828,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "svg" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583e1c5c326fd6fede8797006de3b95ad6bcd60a592952952c5ba7ddd7e84c83" +checksum = "700efb40f3f559c23c18b446e8ed62b08b56b2bb3197b36d57e0470b4102779e" [[package]] name = "svg_path_ops" @@ -3919,9 +4201,9 @@ dependencies = [ [[package]] name = "ttf-parser" -version = "0.20.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" +checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" [[package]] name = "typenum" @@ -4056,9 +4338,9 @@ dependencies = [ [[package]] name = "usvg" -version = "0.41.0" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c704361d822337cfc00387672c7b59eaa72a1f0744f62b2a68aa228a0c6927d" +checksum = "b84ea542ae85c715f07b082438a4231c3760539d902e11d093847a0b22963032" dependencies = [ "base64", "data-url", @@ -4068,7 +4350,7 @@ dependencies = [ "kurbo 0.11.0", "log", "pico-args", - "roxmltree", + "roxmltree 0.20.0", "rustybuzz", "simplecss", "siphasher 1.0.1", @@ -4093,6 +4375,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "v_frame" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + [[package]] name = "valuable" version = "0.1.0" @@ -4281,9 +4574,9 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" dependencies = [ "windows-targets 0.52.5", ] @@ -4570,6 +4863,12 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + [[package]] name = "zune-inflate" version = "0.2.54" @@ -4578,3 +4877,12 @@ checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" dependencies = [ "simd-adler32", ] + +[[package]] +name = "zune-jpeg" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec866b44a2a1fd6133d363f073ca1b179f438f99e7e5bfb1e33f7181facfe448" +dependencies = [ + "zune-core", +] diff --git a/Cargo.toml b/Cargo.toml index aa8c783544..e38df0e6bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,63 +25,63 @@ anyhow = "1.0" approx = "0.5.1" async-fs = "2.1" atty = "0.2.14" -base64 = "0.22.0" -cairo-rs = { version = "0.19.2", features = ["v1_18", "png", "svg", "pdf"] } -chrono = "0.4.34" -clap = { version = "4.4", features = ["derive"] } +base64 = "0.22.1" +cairo-rs = { version = "0.19.4", features = ["v1_18", "png", "svg", "pdf"] } +chrono = "0.4.38" +clap = { version = "4.5", features = ["derive"] } dialoguer = "0.11.0" flate2 = "1.0" fs_extra = "1.3" futures = "0.3.30" geo = "0.28.0" gettext-rs = { version = "0.7.0", features = ["gettext-system"] } -gio = "0.19.2" -glib = "0.19.2" +gio = "0.19.5" +glib = "0.19.7" glib-build-tools = "0.19.0" -gtk4 = { version = "0.8.0", features = ["v4_12"] } +gtk4 = { version = "0.8.2", features = ["v4_12"] } ijson = "0.1.3" -image = "0.24.9" +image = "0.25.1" indicatif = "0.17.8" ink-stroke-modeler-rs = { git = "https://github.com/flxzt/ink-stroke-modeler-rs", rev = "84d311e9b0d034dcd955a1f353d37f54b2bda70f" } -itertools = "0.12.1" +itertools = "0.13.0" kurbo = "0.10.4" -librsvg = "2.58.0-beta.1" -nalgebra = { version = "0.32.4", features = ["serde-serialize"] } +librsvg = "2.58.1" +nalgebra = { version = "0.32.5", features = ["serde-serialize"] } notify-debouncer-full = "0.3.1" num-derive = "0.4.2" -num-traits = "0.2.18" -numeric-sort = "0.1.0" +num-traits = "0.2.19" +numeric-sort = "0.1.1" once_cell = "1.19" opener = {version = "0.7.0", features=["reveal"]} -palette = "0.7.5" -parry2d-f64 = { version = "0.13.6", features = ["serde-serialize"] } +palette = "0.7.6" +parry2d-f64 = { version = "0.15.1", features = ["serde-serialize"] } path-absolutize = "3.1" piet = "0.6.2" piet-cairo = "0.6.2" rand = "0.8.5" rand_distr = "0.4.3" rand_pcg = "0.3.1" -rayon = "1.9" +rayon = "1.10" regex = "1.10" -rodio = { version = "0.17.3", default-features = false, features = [ +rodio = { version = "0.18.1", default-features = false, features = [ "symphonia-wav", ] } rough_piet = "0.6.0" roughr = "0.6.0" -roxmltree = "0.19.0" +roxmltree = "0.20.0" rstar = "0.12.0" semver = { version = "1.0", features = ["serde"] } serde = { version = "1.0", features = ["derive", "rc"] } serde_json = "1.0" slotmap = { version = "1.0", features = ["serde"] } smol = "2.0" -svg = "0.16.0" +svg = "0.17.0" thiserror = "1.0" tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } unicode-segmentation = "1.11" url = "2.5" -usvg = "0.41.0" +usvg = "0.42.0" winresource = "0.1.17" xmlwriter = "0.1.0" # Enabling feature > v20_9 causes linker errors on mingw diff --git a/README.md b/README.md index 8811fd6994..c9075f2479 100644 --- a/README.md +++ b/README.md @@ -176,7 +176,7 @@ To be able to open and export older files that are incompatible with the newest ## License -Rnote is available under the GPL-3.0-or-later. See the LICENSE file for more info. +Rnote is available under GPL-3.0-or-later. See the LICENSE file for more info. Copyright (C) 2023 The Rnote Authors diff --git a/crates/rnote-compose/src/lib.rs b/crates/rnote-compose/src/lib.rs index 5810cb0a7d..bee113aeb4 100644 --- a/crates/rnote-compose/src/lib.rs +++ b/crates/rnote-compose/src/lib.rs @@ -31,6 +31,8 @@ pub mod style; pub mod transform; /// other misc utilities pub mod utils; +/// vertical tool options +pub mod verticaltoolconfig; // Re-exports pub use color::Color; @@ -42,6 +44,7 @@ pub use shapes::Shape; pub use splitorder::SplitOrder; pub use style::Style; pub use transform::Transform; +pub use verticaltoolconfig::VerticalToolConfig; // Renames extern crate nalgebra as na; diff --git a/crates/rnote-compose/src/verticaltoolconfig.rs b/crates/rnote-compose/src/verticaltoolconfig.rs new file mode 100644 index 0000000000..8a1d4b8fca --- /dev/null +++ b/crates/rnote-compose/src/verticaltoolconfig.rs @@ -0,0 +1,12 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +/// configuration for the vertical tool +pub struct VerticalToolConfig { + /// forces the snap behavior for the vertical space tool + pub force_snap: bool, + /// horizontal limit + pub horizontal_border: bool, + /// vertical limit + pub vertical_border: bool, +} diff --git a/crates/rnote-engine/src/document/mod.rs b/crates/rnote-engine/src/document/mod.rs index a2e01da3dd..4dbeb60461 100644 --- a/crates/rnote-engine/src/document/mod.rs +++ b/crates/rnote-engine/src/document/mod.rs @@ -8,6 +8,7 @@ pub use format::Format; // Imports use crate::{Camera, CloneConfig, StrokeStore, WidgetFlags}; +use core::fmt::Display; use p2d::bounding_volume::{Aabb, BoundingVolume}; use rnote_compose::ext::{AabbExt, Vector2Ext}; use rnote_compose::{Color, SplitOrder}; @@ -69,13 +70,13 @@ impl std::str::FromStr for Layout { } } -impl std::string::ToString for Layout { - fn to_string(&self) -> String { +impl Display for Layout { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Layout::FixedSize => String::from("fixed-size"), - Layout::ContinuousVertical => String::from("continuous-vertical"), - Layout::SemiInfinite => String::from("semi-infinite"), - Layout::Infinite => String::from("infinite"), + Layout::FixedSize => write!(f, "fixed-size"), + Layout::ContinuousVertical => write!(f, "continuous-vertical"), + Layout::SemiInfinite => write!(f, "semi-infinite"), + Layout::Infinite => write!(f, "infinite"), } } } @@ -404,12 +405,12 @@ impl Document { /// Snap the position to the document and pattern grid when `snap_positions` is enabled. /// /// If not, the original coordinates are returned. - pub(crate) fn snap_position(&self, pos: na::Vector2) -> na::Vector2 { + pub(crate) fn snap_position(&self, pos: na::Vector2, force: bool) -> na::Vector2 { const DOCUMENT_SNAP_DIST: f64 = 10.; let doc_format_size = self.format.size(); let pattern_size = self.background.pattern_size; - if !self.snap_positions { + if !self.snap_positions && !force { return pos; } diff --git a/crates/rnote-engine/src/engine/export.rs b/crates/rnote-engine/src/engine/export.rs index b2a1a84d34..f51e9256e5 100644 --- a/crates/rnote-engine/src/engine/export.rs +++ b/crates/rnote-engine/src/engine/export.rs @@ -760,10 +760,8 @@ impl Engine { let result = || -> Result>, anyhow::Error> { let image_format = match doc_pages_export_prefs.export_format { DocPagesExportFormat::Svg => return Err(anyhow::anyhow!("Extracting bitmap image format from doc pages export prefs failed, not set to a bitmap format.")), - DocPagesExportFormat::Png => image::ImageOutputFormat::Png, - DocPagesExportFormat::Jpeg => { - image::ImageOutputFormat::Jpeg(doc_pages_export_prefs.jpeg_quality) - } + DocPagesExportFormat::Png => image::ImageFormat::Png, + DocPagesExportFormat::Jpeg => image::ImageFormat::Jpeg, }; pages_contents .into_par_iter() @@ -780,7 +778,10 @@ impl Engine { "Generating Svg for page {i} failed, returned None." ))? .gen_image(doc_pages_export_prefs.bitmap_scalefactor)? - .into_encoded_bytes(image_format.clone()) + .into_encoded_bytes( + image_format, + Some(doc_pages_export_prefs.jpeg_quality), + ) }) .collect() }; @@ -886,15 +887,16 @@ impl Engine { }; let image_format = match selection_export_prefs.export_format { SelectionExportFormat::Svg => return Err(anyhow::anyhow!("Extracting bitmap image format from doc pages export prefs failed, not set to a bitmap format.")), - SelectionExportFormat::Png => image::ImageOutputFormat::Png, - SelectionExportFormat::Jpeg => { - image::ImageOutputFormat::Jpeg(selection_export_prefs.jpeg_quality) - } + SelectionExportFormat::Png => image::ImageFormat::Png, + SelectionExportFormat::Jpeg => image::ImageFormat::Jpeg }; Ok(Some( svg.gen_image(selection_export_prefs.bitmap_scalefactor)? - .into_encoded_bytes(image_format)?, + .into_encoded_bytes( + image_format, + Some(selection_export_prefs.jpeg_quality), + )?, )) }; if oneshot_sender.send(result()).is_err() { diff --git a/crates/rnote-engine/src/pens/mod.rs b/crates/rnote-engine/src/pens/mod.rs index 5d7e904de2..0c3c677abf 100644 --- a/crates/rnote-engine/src/pens/mod.rs +++ b/crates/rnote-engine/src/pens/mod.rs @@ -27,6 +27,7 @@ pub use typewriter::Typewriter; // Imports use crate::engine::{EngineView, EngineViewMut}; use crate::{DrawableOnDoc, WidgetFlags}; +use core::fmt::Display; use futures::channel::oneshot; use piet_cairo::CairoRenderContext; use rnote_compose::penevent::PenProgress; @@ -232,15 +233,15 @@ impl std::str::FromStr for PenStyle { } } -impl std::string::ToString for PenStyle { - fn to_string(&self) -> String { +impl Display for PenStyle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - PenStyle::Brush => String::from("brush"), - PenStyle::Shaper => String::from("shaper"), - PenStyle::Typewriter => String::from("typewriter"), - PenStyle::Eraser => String::from("eraser"), - PenStyle::Selector => String::from("selector"), - PenStyle::Tools => String::from("tools"), + PenStyle::Brush => write!(f, "brush"), + PenStyle::Shaper => write!(f, "shaper"), + PenStyle::Typewriter => write!(f, "typewriter"), + PenStyle::Eraser => write!(f, "eraser"), + PenStyle::Selector => write!(f, "selector"), + PenStyle::Tools => write!(f, "tools"), } } } diff --git a/crates/rnote-engine/src/pens/pensconfig/toolsconfig.rs b/crates/rnote-engine/src/pens/pensconfig/toolsconfig.rs index da015e1dcd..60c94feae9 100644 --- a/crates/rnote-engine/src/pens/pensconfig/toolsconfig.rs +++ b/crates/rnote-engine/src/pens/pensconfig/toolsconfig.rs @@ -1,4 +1,5 @@ // Imports +use rnote_compose::VerticalToolConfig; use serde::{Deserialize, Serialize}; #[derive( @@ -45,4 +46,5 @@ impl TryFrom for ToolStyle { pub struct ToolsConfig { #[serde(rename = "style")] pub style: ToolStyle, + pub vertical_tool_config: VerticalToolConfig, } diff --git a/crates/rnote-engine/src/pens/selector/mod.rs b/crates/rnote-engine/src/pens/selector/mod.rs index 09ab012aa3..230c93ac0e 100644 --- a/crates/rnote-engine/src/pens/selector/mod.rs +++ b/crates/rnote-engine/src/pens/selector/mod.rs @@ -194,7 +194,7 @@ impl PenBehaviour for Selector { // Add rendered Png let image = stroke_content_svg .gen_image(Engine::STROKE_EXPORT_IMAGE_SCALE)? - .into_encoded_bytes(image::ImageOutputFormat::Png)?; + .into_encoded_bytes(image::ImageFormat::Png, None)?; clipboard_content.push((image, String::from("image/png"))); } } @@ -255,7 +255,7 @@ impl PenBehaviour for Selector { // Add rendered Png let image = stroke_content_svg .gen_image(Engine::STROKE_EXPORT_IMAGE_SCALE)? - .into_encoded_bytes(image::ImageOutputFormat::Png)?; + .into_encoded_bytes(image::ImageFormat::Png, None)?; clipboard_content.push((image, String::from("image/png"))); } } diff --git a/crates/rnote-engine/src/pens/selector/penevents.rs b/crates/rnote-engine/src/pens/selector/penevents.rs index 02b001a699..02793adbee 100644 --- a/crates/rnote-engine/src/pens/selector/penevents.rs +++ b/crates/rnote-engine/src/pens/selector/penevents.rs @@ -204,7 +204,7 @@ impl Selector { let offset = engine_view .document - .snap_position(snap_corner_pos + (element.pos - *current_pos)) + .snap_position(snap_corner_pos + (element.pos - *current_pos), false) - snap_corner_pos; if offset.magnitude() @@ -303,7 +303,7 @@ impl Selector { if !lock_aspectratio { offset_to_start = engine_view .document - .snap_position(snap_corner_pos + offset_to_start) + .snap_position(snap_corner_pos + offset_to_start, false) - snap_corner_pos; } offset_to_start = match from_corner { diff --git a/crates/rnote-engine/src/pens/tools.rs b/crates/rnote-engine/src/pens/tools.rs index cbe68e883a..5cd53eddce 100644 --- a/crates/rnote-engine/src/pens/tools.rs +++ b/crates/rnote-engine/src/pens/tools.rs @@ -17,6 +17,7 @@ use std::time::Instant; pub struct VerticalSpaceTool { start_pos_y: f64, pos_y: f64, + pos_x: f64, strokes_below: Vec, } @@ -25,6 +26,7 @@ impl Default for VerticalSpaceTool { Self { start_pos_y: 0.0, pos_y: 0.0, + pos_x: 0.0, strokes_below: vec![], } } @@ -299,10 +301,77 @@ impl PenBehaviour for Tools { ToolStyle::VerticalSpace => { self.verticalspace_tool.start_pos_y = element.pos[1]; self.verticalspace_tool.pos_y = element.pos[1]; + self.verticalspace_tool.pos_x = element.pos[0]; - self.verticalspace_tool.strokes_below = engine_view - .store - .keys_below_y(self.verticalspace_tool.pos_y); + let y_max: Option = if engine_view + .pens_config + .tools_config + .vertical_tool_config + .horizontal_border + { + Some( + ((self.verticalspace_tool.pos_y / engine_view.document.height) + .floor() + + 1.0f64) + * engine_view.document.height, + ) + } else { + None + }; + + let x_max: Option = if engine_view + .pens_config + .tools_config + .vertical_tool_config + .vertical_border + { + Some( + ((self.verticalspace_tool.pos_x / engine_view.document.width) + .floor() + + 1.0f64) + * engine_view.document.width, + ) + } else { + None + }; + + let x_min: Option = if engine_view + .pens_config + .tools_config + .vertical_tool_config + .vertical_border + { + Some( + ((self.verticalspace_tool.pos_x / engine_view.document.width) + .floor()) + * engine_view.document.width, + ) + } else { + None + }; + + println!( + "config : hor {:?}, ver {:?}, xmax {:?} ymax {:?}", + engine_view + .pens_config + .tools_config + .vertical_tool_config + .horizontal_border, + engine_view + .pens_config + .tools_config + .vertical_tool_config + .vertical_border, + x_max, + y_max + ); + + self.verticalspace_tool.strokes_below = engine_view.store.keys_between( + self.verticalspace_tool.pos_y, + y_max, + x_min, + x_max, + ); } ToolStyle::OffsetCamera => { self.offsetcamera_tool.start = element.pos; @@ -348,6 +417,11 @@ impl PenBehaviour for Tools { } else { engine_view.document.snap_position( element.pos - na::vector![0., self.verticalspace_tool.pos_y], + engine_view + .pens_config + .tools_config + .vertical_tool_config + .force_snap, )[1] }; diff --git a/crates/rnote-engine/src/pens/typewriter/penevents.rs b/crates/rnote-engine/src/pens/typewriter/penevents.rs index dd25404e5b..797594975e 100644 --- a/crates/rnote-engine/src/pens/typewriter/penevents.rs +++ b/crates/rnote-engine/src/pens/typewriter/penevents.rs @@ -27,7 +27,7 @@ impl Typewriter { TypewriterState::Idle | TypewriterState::Start { .. } => { let mut refresh_state = false; let mut new_state = - TypewriterState::Start(engine_view.document.snap_position(element.pos)); + TypewriterState::Start(engine_view.document.snap_position(element.pos, false)); if let Some(&stroke_key) = engine_view .store @@ -238,10 +238,10 @@ impl Typewriter { .map(|s| s.bounds()) { let snap_corner_pos = textstroke_bounds.mins.coords; - let offset = engine_view - .document - .snap_position(snap_corner_pos + (element.pos - *current_pos)) - - snap_corner_pos; + let offset = engine_view.document.snap_position( + snap_corner_pos + (element.pos - *current_pos), + false, + ) - snap_corner_pos; if offset.magnitude() > Self::TRANSLATE_OFFSET_THRESHOLD / engine_view.camera.total_zoom() diff --git a/crates/rnote-engine/src/render.rs b/crates/rnote-engine/src/render.rs index a9a51abeea..e8d6963cef 100644 --- a/crates/rnote-engine/src/render.rs +++ b/crates/rnote-engine/src/render.rs @@ -10,13 +10,14 @@ use rnote_compose::shapes::{Rectangle, Shapeable}; use rnote_compose::transform::Transformable; use serde::{Deserialize, Serialize}; use std::io::{self, Cursor}; +use std::sync::Arc; use svg::Node; /// Usvg font database -pub static USVG_FONTDB: Lazy = Lazy::new(|| { +pub static USVG_FONTDB: Lazy> = Lazy::new(|| { let mut db = usvg::fontdb::Database::new(); db.load_system_fonts(); - db + Arc::new(db) }); /// Px unit (96 DPI ) to Point unit ( 72 DPI ) conversion factor. @@ -231,19 +232,37 @@ impl Image { } } + /// Encodes the image into the provided format. + /// + /// When the format is `Jpeg`, the quality should be provided, but falls back to 93 if it is None. pub fn into_encoded_bytes( self, - format: image::ImageOutputFormat, + format: image::ImageFormat, + quality: Option, ) -> Result, anyhow::Error> { + const QUALITY_FALLBACK: u8 = 93; + self.assert_valid()?; let mut bytes_buf: Cursor> = Cursor::new(Vec::new()); let dynamic_image = image::DynamicImage::ImageRgba8( self.into_imgbuf() .context("Converting image to image::ImageBuffer failed.")?, ); - dynamic_image - .write_to(&mut bytes_buf, format) - .context("Writing dynamic image to bytes buffer failed.")?; + match format { + image::ImageFormat::Jpeg => { + image::codecs::jpeg::JpegEncoder::new_with_quality( + &mut bytes_buf, + quality.map(|q| q.clamp(0, 100)).unwrap_or(QUALITY_FALLBACK), + ) + .encode_image(&dynamic_image) + .context("Encode dynamic image to jpeg failed.")?; + } + format => { + dynamic_image + .write_to(&mut bytes_buf, format) + .context("Encode dynamic image to format '{format}' failed.")?; + } + } Ok(bytes_buf.into_inner()) } @@ -436,8 +455,13 @@ impl Svg { false, ); - let usvg_tree = - usvg::Tree::from_str(&svg_data_wrapped, &usvg::Options::default(), &USVG_FONTDB)?; + let usvg_tree = usvg::Tree::from_str( + &svg_data_wrapped, + &usvg::Options { + fontdb: Arc::clone(&USVG_FONTDB), + ..Default::default() + }, + )?; self.svg_data = usvg_tree.to_string(&xml_options); self.bounds = bounds_simplified; diff --git a/crates/rnote-engine/src/store/stroke_comp.rs b/crates/rnote-engine/src/store/stroke_comp.rs index c5f5779363..ce0b76ba93 100644 --- a/crates/rnote-engine/src/store/stroke_comp.rs +++ b/crates/rnote-engine/src/store/stroke_comp.rs @@ -640,6 +640,50 @@ impl StrokeStore { .collect::>() } + pub(crate) fn keys_between( + &self, + y_start: f64, + y_end: Option, + x_start: Option, + x_end: Option, + ) -> Vec { + // match on constraints + match (y_end, x_start, x_end) { + (None, None, None) => return self.keys_below_y(y_start), + (Some(ymax), Some(xmin), Some(xmax)) => { + return self + .stroke_components + .iter() + .filter_map(|(key, stroke)| { + if stroke.bounds().mins[1] > y_start + && stroke.bounds().maxs[1] < ymax + && stroke.bounds().mins[0] > xmin + && stroke.bounds().maxs[0] < xmax + { + Some(key) + } else { + None + } + }) + .collect::>(); + } + (Some(ymax), _, _) => { + return self + .stroke_components + .iter() + .filter_map(|(key, stroke)| { + if stroke.bounds().mins[1] > y_start && stroke.bounds().maxs[1] < ymax { + Some(key) + } else { + None + } + }) + .collect::>(); + } + _ => Vec::::new(), + } + } + pub(crate) fn filter_keys_intersecting_bounds<'a, I: IntoIterator>( &'a self, keys: I, diff --git a/crates/rnote-engine/src/strokes/content.rs b/crates/rnote-engine/src/strokes/content.rs index 1594bd991b..050671ee8f 100644 --- a/crates/rnote-engine/src/strokes/content.rs +++ b/crates/rnote-engine/src/strokes/content.rs @@ -107,7 +107,7 @@ where /// Export to encoded bitmap image (Png/Jpeg/..). fn export_to_bitmap_image_bytes( &self, - format: image::ImageOutputFormat, + format: image::ImageFormat, image_scale: f64, ) -> Result, anyhow::Error> { render::Image::gen_with_piet( @@ -115,6 +115,6 @@ where self.bounds(), image_scale, )? - .into_encoded_bytes(format) + .into_encoded_bytes(format, None) } } diff --git a/crates/rnote-engine/src/strokes/stroke.rs b/crates/rnote-engine/src/strokes/stroke.rs index dc9f794c75..bf67e07883 100644 --- a/crates/rnote-engine/src/strokes/stroke.rs +++ b/crates/rnote-engine/src/strokes/stroke.rs @@ -496,7 +496,7 @@ impl Stroke { } Stroke::ShapeStroke(shapestroke) => { let png_data = match shapestroke.export_to_bitmap_image_bytes( - image::ImageOutputFormat::Png, + image::ImageFormat::Png, Engine::STROKE_EXPORT_IMAGE_SCALE, ) { Ok(image_bytes) => image_bytes, @@ -540,7 +540,7 @@ impl Stroke { // Xournal++ text strokes do not support affine transformations, so we have to convert on best effort here. // The best solution for now seems to be to export them as a bitmap image. let png_data = match textstroke.export_to_bitmap_image_bytes( - image::ImageOutputFormat::Png, + image::ImageFormat::Png, Engine::STROKE_EXPORT_IMAGE_SCALE, ) { Ok(image_bytes) => image_bytes, @@ -582,7 +582,7 @@ impl Stroke { } Stroke::VectorImage(vectorimage) => { let png_data = match vectorimage.export_to_bitmap_image_bytes( - image::ImageOutputFormat::Png, + image::ImageFormat::Png, Engine::STROKE_EXPORT_IMAGE_SCALE, ) { Ok(image_bytes) => image_bytes, @@ -626,7 +626,7 @@ impl Stroke { } Stroke::BitmapImage(bitmapimage) => { let png_data = match bitmapimage.export_to_bitmap_image_bytes( - image::ImageOutputFormat::Png, + image::ImageFormat::Png, Engine::STROKE_EXPORT_IMAGE_SCALE, ) { Ok(image_bytes) => image_bytes, diff --git a/crates/rnote-engine/src/strokes/vectorimage.rs b/crates/rnote-engine/src/strokes/vectorimage.rs index 5b5ed49ad0..131aac738d 100644 --- a/crates/rnote-engine/src/strokes/vectorimage.rs +++ b/crates/rnote-engine/src/strokes/vectorimage.rs @@ -16,6 +16,7 @@ use rnote_compose::transform::Transform; use rnote_compose::transform::Transformable; use serde::{Deserialize, Serialize}; use std::ops::Range; +use std::sync::Arc; #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(default, rename = "vectorimage")] @@ -152,8 +153,13 @@ impl VectorImage { indent: xmlwriter::Indent::None, attributes_indent: xmlwriter::Indent::None, }; - let svg_tree = - usvg::Tree::from_str(svg_data, &usvg::Options::default(), &render::USVG_FONTDB)?; + let svg_tree = usvg::Tree::from_str( + svg_data, + &usvg::Options { + fontdb: Arc::clone(&render::USVG_FONTDB), + ..Default::default() + }, + )?; let intrinsic_size = na::vector![ svg_tree.size().width() as f64, diff --git a/crates/rnote-ui/data/app.metainfo.xml.in.in b/crates/rnote-ui/data/app.metainfo.xml.in.in index ac9c333acd..c1404b89d4 100644 --- a/crates/rnote-ui/data/app.metainfo.xml.in.in +++ b/crates/rnote-ui/data/app.metainfo.xml.in.in @@ -3,7 +3,7 @@ @APP_ID@ CC0 - GPL-3.0 + GPL-3.0-or-later @APP_NAME_CAPITALIZED@ @APP_ID@.desktop Sketch and take handwritten notes diff --git a/crates/rnote-ui/data/ui/penssidebar/toolspage.ui b/crates/rnote-ui/data/ui/penssidebar/toolspage.ui index c809c908ca..69fd3b5bbb 100644 --- a/crates/rnote-ui/data/ui/penssidebar/toolspage.ui +++ b/crates/rnote-ui/data/ui/penssidebar/toolspage.ui @@ -46,7 +46,91 @@ + + + vertical + + + settings-symbolic + left + Vertical Toolspace Configuration + verticaltool_popover + + + + + + + + + + vertical + 6 + 6 + 6 + 6 + 12 + + + + + Shape Configuration + true + center + + + + + + window-close-symbolic + + + + + + + + 300 + none + + + + Force snapping + Force the vertical space tool to +snap to the grid + + + + + Respect Vertical Borders + Only select elements that are inside or below the +page selected, not ones on the left/right + + + + + respect Horizontal Borders + Only select elements between the current position +and the next horizontal border below + + + + + + + \ No newline at end of file diff --git a/crates/rnote-ui/src/appwindow/appsettings.rs b/crates/rnote-ui/src/appwindow/appsettings.rs index 15fd69d2f5..d8d709be32 100644 --- a/crates/rnote-ui/src/appwindow/appsettings.rs +++ b/crates/rnote-ui/src/appwindow/appsettings.rs @@ -412,9 +412,6 @@ impl RnAppWindow { let is_maximized = app_settings.boolean("is-maximized"); if is_maximized { - // don't restore maximized window state on macos, avoids issues discussed in - // issue 823 - https://github.com/flxzt/rnote/issues/823 - #[cfg(not(target_os = "macos"))] self.maximize(); } else { self.set_default_size(window_width, window_height); @@ -446,9 +443,17 @@ impl RnAppWindow { { // Appwindow - app_settings.set_int("window-width", self.width())?; - app_settings.set_int("window-height", self.height())?; - app_settings.set_boolean("is-maximized", self.is_maximized())?; + match self.is_maximized() { + false => { + app_settings.set_int("window-width", self.width())?; + app_settings.set_int("window-height", self.height())?; + app_settings.set_boolean("is-maximized", self.is_maximized())?; + } + true => { + // this way we don't force the window to be the same size as the last maximized window + app_settings.set_boolean("is-maximized", self.is_maximized())?; + } + } } { diff --git a/crates/rnote-ui/src/appwindow/imp.rs b/crates/rnote-ui/src/appwindow/imp.rs index 0eb74f8bc6..c1e8c7a7f2 100644 --- a/crates/rnote-ui/src/appwindow/imp.rs +++ b/crates/rnote-ui/src/appwindow/imp.rs @@ -556,6 +556,11 @@ impl RnAppWindow { .eraser_page() .stroke_width_picker() .set_position(PositionType::Left); + obj.overlays() + .penssidebar() + .tools_page() + .verticaltool_menubutton() + .set_direction(ArrowType::Right); } else { obj.split_view().set_sidebar_position(PackType::End); obj.main_header() @@ -678,6 +683,11 @@ impl RnAppWindow { .eraser_page() .stroke_width_picker() .set_position(PositionType::Right); + obj.overlays() + .penssidebar() + .tools_page() + .verticaltool_menubutton() + .set_direction(ArrowType::Left); } } } diff --git a/crates/rnote-ui/src/canvas/input.rs b/crates/rnote-ui/src/canvas/input.rs index a0c96c9060..da423633e6 100644 --- a/crates/rnote-ui/src/canvas/input.rs +++ b/crates/rnote-ui/src/canvas/input.rs @@ -119,7 +119,14 @@ pub(crate) fn handle_pointer_controller_event( if gdk_button == gdk::BUTTON_PRIMARY { pen_state = PenState::Up; } else { - pen_state = PenState::Proximity; + #[cfg(target_os = "windows")] + { + pen_state = PenState::Up; + } + #[cfg(not(target_os = "windows"))] + { + pen_state = PenState::Proximity; + } } } else { #[allow(clippy::collapsible_else_if)] @@ -168,6 +175,13 @@ pub(crate) fn handle_pointer_controller_event( for (element, event_time) in elements { tracing::trace!("handle pen event element - element: {element:?}, pen_state: {pen_state:?}, event_time_delta: {:?}, modifier_keys: {modifier_keys:?}, pen_mode: {pen_mode:?}", now.duration_since(event_time)); + #[cfg(target_os = "windows")] + { + if element.pressure > 0.0 && is_stylus { + pen_state = PenState::Down; + } + } + match pen_state { PenState::Up => { canvas.enable_drawing_cursor(false); diff --git a/crates/rnote-ui/src/dialogs/export.rs b/crates/rnote-ui/src/dialogs/export.rs index 9deb846672..81301cd793 100644 --- a/crates/rnote-ui/src/dialogs/export.rs +++ b/crates/rnote-ui/src/dialogs/export.rs @@ -22,7 +22,7 @@ use std::rc::Rc; pub(crate) async fn dialog_save_doc_as(appwindow: &RnAppWindow, canvas: &RnCanvas) { let filter = FileFilter::new(); - filter.add_mime_type("application/rnote"); + filter.add_pattern("*.rnote"); filter.add_suffix("rnote"); filter.set_name(Some(&gettext(".rnote"))); @@ -278,17 +278,17 @@ fn create_filedialog_export_doc( let filter = FileFilter::new(); match doc_export_prefs.export_format { DocExportFormat::Svg => { - filter.add_mime_type("image/svg+xml"); + filter.add_pattern("*.svg"); filter.add_suffix("svg"); filter.set_name(Some(&gettext("Svg"))); } DocExportFormat::Pdf => { - filter.add_mime_type("application/pdf"); + filter.add_pattern("*.pdf"); filter.add_suffix("pdf"); filter.set_name(Some(&gettext("Pdf"))); } DocExportFormat::Xopp => { - filter.add_mime_type("application/x-xopp"); + filter.add_pattern("*.xopp"); filter.add_suffix("xopp"); filter.set_name(Some(&gettext("Xopp"))); } @@ -583,27 +583,23 @@ fn create_filedialog_export_doc_pages( filter.add_mime_type("inode/directory"); match doc_pages_export_prefs.export_format { DocPagesExportFormat::Svg => { - filter.add_mime_type("image/svg+xml"); + filter.add_pattern("*.svg"); filter.add_suffix("svg"); filter.set_name(Some(&gettext("Svg"))); } DocPagesExportFormat::Png => { - filter.add_mime_type("image/png"); + filter.add_pattern("*.png"); filter.add_suffix("png"); filter.set_name(Some(&gettext("Png"))); } DocPagesExportFormat::Jpeg => { - filter.add_mime_type("image/jpeg"); + filter.add_pattern("*.jpg"); + filter.add_pattern("*.jpeg"); filter.add_suffix("jpg"); filter.add_suffix("jpeg"); filter.set_name(Some(&gettext("Jpeg"))); } } - - let filter_list = gio::ListStore::new::(); - filter_list.append(&filter); - filedialog.set_filters(Some(&filter_list)); - filedialog.set_default_filter(Some(&filter)); filedialog @@ -874,17 +870,18 @@ fn create_filedialog_export_selection( let filter = FileFilter::new(); match selection_export_prefs.export_format { SelectionExportFormat::Svg => { - filter.add_mime_type("image/svg+xml"); + filter.add_pattern("*.svg"); filter.add_suffix("svg"); filter.set_name(Some(&gettext("Svg"))); } SelectionExportFormat::Png => { - filter.add_mime_type("image/png"); + filter.add_pattern("*.png"); filter.add_suffix("png"); filter.set_name(Some(&gettext("Png"))); } SelectionExportFormat::Jpeg => { - filter.add_mime_type("image/jpeg"); + filter.add_pattern("*.jpg"); + filter.add_pattern("*.jpeg"); filter.add_suffix("jpg"); filter.add_suffix("jpeg"); filter.set_name(Some(&gettext("Jpeg"))); @@ -909,7 +906,7 @@ fn create_filedialog_export_selection( pub(crate) async fn filechooser_export_engine_state(appwindow: &RnAppWindow, canvas: &RnCanvas) { let filter = FileFilter::new(); - filter.add_mime_type("application/json"); + filter.add_pattern("*.json"); filter.add_suffix("json"); filter.set_name(Some(&gettext("Json"))); @@ -962,7 +959,7 @@ pub(crate) async fn filechooser_export_engine_state(appwindow: &RnAppWindow, can pub(crate) async fn filechooser_export_engine_config(appwindow: &RnAppWindow, canvas: &RnCanvas) { let filter = FileFilter::new(); - filter.add_mime_type("application/json"); + filter.add_pattern("*.json"); filter.add_suffix("json"); filter.set_name(Some(&gettext("Json"))); diff --git a/crates/rnote-ui/src/dialogs/import.rs b/crates/rnote-ui/src/dialogs/import.rs index bd2f5c2e50..6f90209448 100644 --- a/crates/rnote-ui/src/dialogs/import.rs +++ b/crates/rnote-ui/src/dialogs/import.rs @@ -16,8 +16,8 @@ use rnote_engine::engine::import::{PdfImportPageSpacing, PdfImportPagesType}; /// Opens a new rnote save file in a new tab pub(crate) async fn filedialog_open_doc(appwindow: &RnAppWindow) { let filter = FileFilter::new(); - filter.add_mime_type("application/rnote"); filter.add_suffix("rnote"); + filter.add_pattern("*.rnote"); filter.set_name(Some(&gettext(".rnote"))); let filter_list = gio::ListStore::new::(); @@ -51,12 +51,12 @@ pub(crate) async fn filedialog_open_doc(appwindow: &RnAppWindow) { pub(crate) async fn filedialog_import_file(appwindow: &RnAppWindow) { let filter = FileFilter::new(); - filter.add_mime_type("application/x-xopp"); - filter.add_mime_type("application/pdf"); - filter.add_mime_type("image/svg+xml"); - filter.add_mime_type("image/png"); - filter.add_mime_type("image/jpeg"); - filter.add_mime_type("text/plain"); + filter.add_pattern("*.xopp"); + filter.add_pattern("*.pdf"); + filter.add_pattern("*.svg"); + filter.add_pattern("*.png"); + filter.add_pattern("*.jpeg"); + filter.add_pattern("*.txt"); filter.add_suffix("xopp"); filter.add_suffix("pdf"); filter.add_suffix("svg"); diff --git a/crates/rnote-ui/src/penssidebar/toolspage.rs b/crates/rnote-ui/src/penssidebar/toolspage.rs index bc911425b8..749f9b0736 100644 --- a/crates/rnote-ui/src/penssidebar/toolspage.rs +++ b/crates/rnote-ui/src/penssidebar/toolspage.rs @@ -1,6 +1,9 @@ // Imports use crate::{RnAppWindow, RnCanvasWrapper}; -use gtk4::{glib, glib::clone, prelude::*, subclass::prelude::*, CompositeTemplate, ToggleButton}; +use gtk4::{ + glib, glib::clone, prelude::*, subclass::prelude::*, Button, CompositeTemplate, MenuButton, + Popover, ToggleButton, +}; use rnote_engine::pens::pensconfig::toolsconfig::ToolStyle; mod imp { @@ -15,6 +18,18 @@ mod imp { pub(crate) toolstyle_offsetcamera_toggle: TemplateChild, #[template_child] pub(crate) toolstyle_zoom_toggle: TemplateChild, + #[template_child] + pub(crate) verticaltool_menubutton: TemplateChild, + #[template_child] + pub(crate) verticaltool_popover: TemplateChild, + #[template_child] + pub(crate) verticaltool_popover_close_button: TemplateChild