Basic Open/Save mechanism, with hardcoded file name

This commit is contained in:
Enrico Lumetti 2020-11-09 15:33:22 +01:00
parent 8f7e073c4c
commit e6885125af
4 changed files with 152 additions and 114 deletions

106
Cargo.lock generated
View File

@ -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"

View File

@ -5,12 +5,12 @@ authors = ["Enrico Lumetti <enrico.lumetti@gmail.com>"]
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"] }

View File

@ -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 <https://www.gnu.org/licenses/>.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<CanvasElement>;
@ -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<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
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<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>
{
deserializer.deserialize_seq(PathDeserializer)
}
}
impl Serialize for Path {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<CanvasElement>,
}

View File

@ -14,17 +14,18 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
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<CanvasElement>,
//elements: Vector<CanvasElement>,
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<CanvasData> for CanvasWidget {
fn build_ui() -> impl Widget<CanvasData> {
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<DocumentSnapshot, serde_json::Error> = 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)