Basic Open/Save mechanism #6
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"] }
|
||||
|
|
|
|||
92
src/lib.rs
92
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 <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>,
|
||||
}
|
||||
|
|
|
|||
60
src/main.rs
60
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 <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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue