From e6885125af3a96d46ab0979a7891dd9991aad3d9 Mon Sep 17 00:00:00 2001 From: Enrico Lumetti Date: Mon, 9 Nov 2020 15:33:22 +0100 Subject: [PATCH] Basic Open/Save mechanism, with hardcoded file name --- Cargo.lock | 106 +++------------------------------------------------- Cargo.toml | 8 ++-- src/lib.rs | 92 ++++++++++++++++++++++++++++++++++++++++++--- src/main.rs | 60 ++++++++++++++++++++++++++--- 4 files changed, 152 insertions(+), 114 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a7cecd2..48b2189 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -88,18 +88,6 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9" -[[package]] -name = "bstr" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "473fc6b38233f9af7baa94fb5852dca389e3d95b8e21c8e3719301462c5d9faf" -dependencies = [ - "lazy_static", - "memchr", - "regex-automata", - "serde", -] - [[package]] name = "bumpalo" version = "3.4.0" @@ -226,28 +214,6 @@ dependencies = [ "libc", ] -[[package]] -name = "csv" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4666154fd004af3fd6f1da2e81a96fd5a81927fe8ddb6ecc79e2aa6e138b54" -dependencies = [ - "bstr", - "csv-core", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "csv-core" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" -dependencies = [ - "memchr", -] - [[package]] name = "direct2d" version = "0.2.0" @@ -370,27 +336,6 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" -[[package]] -name = "errno" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa68f2fb9cae9d37c9b2b3584aba698a2e97f72d7aef7b9f7aa71d8b54ce46fe" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067" -dependencies = [ - "gcc", - "libc", -] - [[package]] name = "fluent-bundle" version = "0.12.0" @@ -526,12 +471,6 @@ dependencies = [ "byteorder", ] -[[package]] -name = "gcc" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" - [[package]] name = "gdk" version = "0.12.1" @@ -802,16 +741,6 @@ version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" -[[package]] -name = "libudev-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" -dependencies = [ - "libc", - "pkg-config", -] - [[package]] name = "log" version = "0.4.11" @@ -830,12 +759,6 @@ dependencies = [ "libc", ] -[[package]] -name = "memchr" -version = "2.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" - [[package]] name = "num" version = "0.1.42" @@ -1151,15 +1074,6 @@ dependencies = [ "rand_core 0.3.1", ] -[[package]] -name = "regex-automata" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" -dependencies = [ - "byteorder", -] - [[package]] name = "rental" version = "0.5.5" @@ -1222,6 +1136,9 @@ name = "serde" version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" +dependencies = [ + "serde_derive", +] [[package]] name = "serde_derive" @@ -1352,15 +1269,14 @@ checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" name = "stiletto" version = "0.1.0" dependencies = [ - "csv", "druid", - "errno", "gdk", "gio", "gtk", "im", - "libc", - "udev", + "log", + "serde", + "serde_json", ] [[package]] @@ -1433,16 +1349,6 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" -[[package]] -name = "udev" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47504d1a49b2ea1b133e7ddd1d9f0a83cf03feb9b440c2c470d06db4589cf301" -dependencies = [ - "libc", - "libudev-sys", -] - [[package]] name = "unic-langid" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index 39772e3..dc2905d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,12 +5,12 @@ authors = ["Enrico Lumetti "] edition = "2018" [dependencies] -udev = "0.2" -libc = "0.2" -errno = "0.2" -csv = "1.1" +log = "0.4" druid = { version = "0.6.0", features = ["im"] } im = { version = "*" } +serde = { version = "1.0", features = ["derive"] } +#serde_bare = "0.3.0" +serde_json = "1.0" [patch.crates-io] druid = { git = "https://github.com/doppioandante/druid", branch = "stylus_events_0.6.0", features = ["im"] } diff --git a/src/lib.rs b/src/lib.rs index bf2baa7..dd6c48e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,32 @@ -#[derive(Clone, druid::Data)] +// Stiletto +// Copyright (C) 2020 Stiletto Authors +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see .use serde::{Serialize, Deserialize}; + +use im::Vector; + +use serde::{Serialize, Deserialize}; +use serde::ser::{Serializer}; +use serde::de::{Deserializer, Visitor, SeqAccess}; +use std::vec::{Vec}; + +#[derive(Debug, Clone, druid::Data)] pub struct Path { pub kurbo_path: druid::kurbo::BezPath, } -#[derive(Clone, druid::Data)] +#[derive(Debug, Clone, druid::Data, Serialize, Deserialize)] pub enum CanvasElement { Freehand { path: Path, thickness: f64 }, } @@ -36,8 +59,6 @@ impl CanvasElement { } } -use im::Vector; - // A canvas contains all elements to be drawn pub type Canvas = Vector; @@ -59,7 +80,6 @@ impl VersionedCanvas { // Get current canvas version pub fn get(&self) -> &Canvas { - let focus = self.versions.focus(); self.versions.get(self.curr_version).unwrap() } @@ -110,3 +130,65 @@ impl VersionedCanvas { } } +struct PathDeserializer; + +impl<'de> Visitor<'de> for PathDeserializer { + type Value = Path; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "A sequence of 2D points") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de> + { + use druid::kurbo::BezPath; + + let mut kurbo_path = BezPath::new(); + let mut first_element = true; + while let Some(point) = seq.next_element::<(f64, f64)>()? { + if first_element { + kurbo_path.move_to(point); + first_element = false; + } else { + kurbo_path.line_to(point); + } + } + + Ok(Path{ kurbo_path }) + } +} + +impl<'de> Deserialize<'de> for Path { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de> + { + deserializer.deserialize_seq(PathDeserializer) + } +} + +impl Serialize for Path { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + use druid::kurbo::{PathEl}; + + serializer.collect_seq( + self.kurbo_path.iter() + .filter_map(|path_el| { + match path_el { + PathEl::MoveTo(pt) => Some(Into::<(f64, f64)>::into(pt)), + PathEl::LineTo(pt) => Some(Into::<(f64, f64)>::into(pt)), + _ => None + } + })) + } +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct DocumentSnapshot { + pub canvas_elements: Vec, +} diff --git a/src/main.rs b/src/main.rs index fa001d2..9d4b1d8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,17 +14,18 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -use druid::im::{vector}; +use log::warn; + +use druid::im::{vector, Vector}; use druid::kurbo::BezPath; use druid::widget::prelude::*; use druid::{AppLauncher, Color, Data, Event, LocalizedString, WindowDesc}; -use stiletto::{CanvasElement, VersionedCanvas, Canvas}; +use stiletto::{CanvasElement, VersionedCanvas, Canvas, DocumentSnapshot}; #[derive(Clone, Data)] struct CanvasData { current_element: Option, - //elements: Vector, elements: VersionedCanvas, } @@ -44,6 +45,17 @@ impl CanvasData { self.elements.redo(); } } + + fn get_document_snapshot(&self) -> DocumentSnapshot { + DocumentSnapshot { + canvas_elements: self.elements.get().iter().cloned().collect() + } + } + + fn set_from_snapshot(&mut self, snapshot: DocumentSnapshot) { + self.current_element = None; + self.elements = VersionedCanvas::new(Vector::from(snapshot.canvas_elements)); + } } struct CanvasWidget; @@ -154,14 +166,52 @@ impl Widget for CanvasWidget { fn build_ui() -> impl Widget { use druid::widget::{Align, Button, CrossAxisAlignment, Flex, SizedBox}; - let toolbar = Flex::row() + let history_buttons = Flex::row() .cross_axis_alignment(CrossAxisAlignment::Center) - .with_spacer(30.0) .with_child( Button::new("Undo").on_click(|_ctx: &mut EventCtx, data: &mut CanvasData, _env: &Env| data.perform_undo()) ) .with_child(Button::new("Redo").on_click(|_ctx: &mut EventCtx, data: &mut CanvasData, _env: &Env| data.perform_redo())); + let save_buttons = Flex::row() + .cross_axis_alignment(CrossAxisAlignment::Center) + .with_child( + Button::new("Open") + .on_click(|_ctx, data: &mut CanvasData, _env| { + use std::fs::File; + + if let Ok(f) = File::open("test.stlt") { + let res_snapshot: Result = serde_json::from_reader(f); + if let Ok(document_snapshot) = res_snapshot { + data.set_from_snapshot(document_snapshot); + } else { + warn!("didn't work: {:?}", res_snapshot.err()); + } + } + })) + .with_child( + Button::new("Save") + .on_click(|_ctx, data: &mut CanvasData, _env| { + use std::fs::File; + + let res_file = File::create("test.stlt"); + if let Ok(f) = res_file { + let write_res = serde_json::to_writer_pretty(f, &data.get_document_snapshot()); + if write_res.is_err() { + warn!("Error while saving: {:?}", write_res.err()); + } + } else { + warn!("Cannot create file: {:?}", res_file.err()); + } + })); + + let toolbar = Flex::row() + .cross_axis_alignment(CrossAxisAlignment::Center) + .with_spacer(30.0) + .with_flex_child(Align::left(history_buttons), 1.0) + .with_flex_child(Align::right(save_buttons), 1.0) + .with_spacer(30.0); + Flex::column() .cross_axis_alignment(CrossAxisAlignment::Center) .must_fill_main_axis(true)