~vpzom/savg

6d6bddc596af186f20209f435b6d889416690bfc — Colin Reeder 5 years ago 2670797
Tools & stuff
6 files changed, 206 insertions(+), 32 deletions(-)

M Cargo.lock
M Cargo.toml
M src/document.rs
M src/main.rs
M src/save.rs
M src/types.rs
M Cargo.lock => Cargo.lock +54 -0
@@ 71,6 71,14 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "encoding_rs"
version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 104,6 112,20 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
 "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "gdk"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 350,6 372,31 @@ dependencies = [
]

[[package]]
name = "rand"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
 "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
 "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
 "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
 "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "rand_core"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
 "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "rand_core"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "rustc-demangle"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 364,6 411,7 @@ dependencies = [
 "glib 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
 "gtk 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
 "quick-xml 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
 "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]


@@ 425,10 473,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d25596627380be4381247dba06c69ad05ca21b3b065bd9827e416882ac41dcd2"
"checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16"
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum encoding_rs 0.8.10 (registry+https://github.com/rust-lang/crates.io-index)" = "065f4d0c826fdaef059ac45487169d918558e3cf86c9d89f6e81cf52369126e5"
"checksum failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6dd377bcc1b1b7ce911967e3ec24fa19c3224394ec05b54aa7b083d498341ac7"
"checksum failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596"
"checksum fragile 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f8140122fa0d5dcb9fc8627cfce2b37cc1500f752636d46ea28bc26785c2f9"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum gdk 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc52c7244046df9d959df87289f1fc5cca23f9f850bab0c967963e2ecb83a96"
"checksum gdk-pixbuf 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc3aa730cb4df3de5d9fed59f43afdf9e5fb2d3d10bfcbd04cec031435ce87f5"
"checksum gdk-pixbuf-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08284f16ce4d909b10d785a763ba190e222d2c1557b29908bf0a661e27a8ac3b"


@@ 450,6 501,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "3d7b7eaaa90b4a90a932a9ea6666c95a389e424eff347f0f793979289429feee"
"checksum quick-xml 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b62a38216952bff95085ad664b772492288a96a25544f48d25e7200de1d7db7"
"checksum quote 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "63b5829244f52738cfee93b3a165c1911388675be000c888d2fae620dee8fa5b"
"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c"
"checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372"
"checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db"
"checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395"
"checksum syn 0.15.15 (registry+https://github.com/rust-lang/crates.io-index)" = "0a9c2bf1e53c21704a7cce1b2a42768f1ae32a6777108a0d7f1faa4bfe7f7c04"
"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"

M Cargo.toml => Cargo.toml +1 -0
@@ 10,3 10,4 @@ gdk = "0.9.0"
glib = "0.6.0"
fragile = "0.3.0"
quick-xml = "0.13.1"
rand = "0.5.5"

M src/document.rs => src/document.rs +29 -1
@@ 3,8 3,10 @@ use cairo;
use color;
use types::*;

use rand::Rng;

pub struct Document {
    pub content: Vec<Element>,
    pub content: Vec<Object<Element>>,
    pub size: Size2,
}



@@ 17,6 19,32 @@ impl Document {
    }
}

pub struct Object<T> {
    id: u64,
    value: T,
}

impl<T> Object<T> {
    pub fn id(&self) -> u64 {
        self.id
    }
    pub fn new(id: u64, value: T) -> Self {
        Self {
            id,
            value,
        }
    }
    pub fn new_random_id(value: T) -> Self {
        Object::new(rand::thread_rng().gen(), value)
    }
}

impl<T> AsRef<T> for Object<T> {
    fn as_ref(&self) -> &T {
        &self.value
    }
}

pub enum Element {
    Rect(Rect)
}

M src/main.rs => src/main.rs +115 -30
@@ 3,6 3,7 @@ extern crate fragile;
extern crate gdk;
extern crate gtk;
extern crate quick_xml;
extern crate rand;

mod color;
mod document;


@@ 22,12 23,50 @@ use std::sync::atomic::AtomicBool;
fn main() {
    gtk::init().expect("Failed to init GTK");

    let instance = Arc::new(EditInstance {
        document: RwLock::new(Document::new(Size2::new(400.0, 400.0))),
        repaint: AtomicBool::new(false),
        selection: RwLock::new(None),
        tool: RwLock::new(ToolMode::Select),
    });

    let area = gtk::DrawingArea::new();
    area.set_events(area.get_events() | (
            gdk::EventMask::POINTER_MOTION_MASK |
            gdk::EventMask::BUTTON_PRESS_MASK
            ).bits() as i32);

    let toolbar = gtk::Box::new(gtk::Orientation::Vertical, 2);
    {
        let select = gtk::RadioToolButton::new_from_stock(&gtk::STOCK_EDIT);
        toolbar.pack_start(&select, false, false, 0);

        {
            let instance = instance.clone();
            select.connect_clicked(move |_| {
                *instance.tool.write().unwrap() = ToolMode::Select;
            });
        }

        for (tool, icon) in &[
            (ToolMode::Rectangle, &gtk::STOCK_ADD)
        ] {
            let instance = instance.clone();
            let tool = *tool;

            let btn = gtk::RadioToolButton::new_with_stock_from_widget(&select, icon);
            toolbar.pack_start(&btn, false, false, 0);

            btn.connect_clicked(move |_| {
                *instance.tool.write().unwrap() = tool;
            });
        }
    }

    let content_box = gtk::Box::new(gtk::Orientation::Horizontal, 2);
    content_box.pack_start(&toolbar, false, false, 0);
    content_box.pack_end(&area, true, true, 0);

    let save_item = gtk::MenuItem::new_with_label("Save");

    let file_menu = gtk::Menu::new();


@@ 39,20 78,15 @@ fn main() {
    let menu_bar = gtk::MenuBar::new();
    menu_bar.append(&file_item);

    let main_box = gtk::Box::new(gtk::Orientation::Vertical, 2);
    main_box.pack_start(&menu_bar, false, false, 0);
    main_box.pack_end(&area, true, true, 0);
    let root_box = gtk::Box::new(gtk::Orientation::Vertical, 2);
    root_box.pack_start(&menu_bar, false, false, 0);
    root_box.pack_end(&content_box, true, true, 0);

    let window = gtk::Window::new(gtk::WindowType::Toplevel);
    window.add(&main_box);
    window.add(&root_box);
    window.set_title("Savg");
    window.show_all();

    let instance = Arc::new(EditInstance {
        document: RwLock::new(Document::new(Size2::new(400.0, 400.0))),
        repaint: AtomicBool::new(false),
    });

    {
        let instance = instance.clone();
        area.connect_draw(move |_, ctx| {


@@ 61,11 95,30 @@ fn main() {
            {
                let document = instance.document.read().unwrap();

                let selection = {
                    let selection = instance.selection.read().unwrap();
                    let selection: &Option<_> = &selection;
                    if let Some(selection) = selection {
                        document.content.iter().find(|i| i.id() == *selection)
                    }
                    else {
                        None
                    }
                };

                ctx.rectangle(0.0, 0.0, document.size.width, document.size.height);
                ctx.fill();

                for elem in &document.content {
                    elem.draw(ctx);
                    elem.as_ref().draw(ctx);
                }

                if let Some(selection) = selection {
                    let bbox = selection.as_ref().get_aabb();
                    ctx.set_source_rgb(0.0, 0.0, 0.0);
                    ctx.set_dash(&mut [5.0], 0.0);
                    ctx.rectangle(bbox.position.x, bbox.position.y, bbox.size.width, bbox.size.height);
                    ctx.stroke();
                }
            }



@@ 78,29 131,47 @@ fn main() {
        area.connect_button_press_event(move |_, ev| {
            let (pos_x, pos_y) = ev.get_position();

            {
                let mut document = instance.document.write().unwrap();

                document.content.push(Element::Rect(Rect {
                    position: Point2 {
            let tool = instance.tool.read().unwrap();
            let tool: &ToolMode = &tool;
            match tool {
                ToolMode::Rectangle => {
                    let mut document = instance.document.write().unwrap();

                    document.content.push(Object::new_random_id(Element::Rect(Rect {
                        position: Point2 {
                            x: pos_x,
                            y: pos_y,
                        },
                        presentation: Presentation {
                            fill: Some(Paint::Color(color::Color::RGB(color::RGB {
                                r: 0,
                                g: 0,
                                b: 255,
                            }))),
                        },
                        size: Size2 {
                            width: 100.0,
                            height: 50.0,
                        },
                    })));
                    instance.queue_repaint();
                },
                ToolMode::Select => {
                    let document = instance.document.read().unwrap();

                    let new_selection = document.content.iter().find(|elem| elem.as_ref().get_aabb().contains(Point2 {
                        x: pos_x,
                        y: pos_y,
                    },
                    presentation: Presentation {
                        fill: Some(Paint::Color(color::Color::RGB(color::RGB {
                            r: 0,
                            g: 0,
                            b: 255,
                        }))),
                    },
                    size: Size2 {
                        width: 100.0,
                        height: 50.0,
                    },
                }));
            }
                    }));

                    *instance.selection.write().unwrap() = match new_selection {
                        Some(e) => Some(e.id()),
                        None => None
                    };

            instance.repaint.store(true, std::sync::atomic::Ordering::Release);
                    instance.queue_repaint();
                },
            }

            Inhibit(true)
        });


@@ 167,4 238,18 @@ fn main() {
struct EditInstance {
    document: RwLock<Document>,
    repaint: AtomicBool,
    selection: RwLock<Option<u64>>,
    tool: RwLock<ToolMode>,
}

impl EditInstance {
    pub fn queue_repaint(&self) {
        self.repaint.store(true, std::sync::atomic::Ordering::Release);
    }
}

#[derive(Clone, Copy)]
enum ToolMode {
    Select,
    Rectangle,
}

M src/save.rs => src/save.rs +1 -1
@@ 36,7 36,7 @@ pub fn save_svg(doc: &document::Document, dest: impl std::io::Write) -> Result<(
                                                       .with_attributes(root_attrs.into_iter())))?;

    for elem in &doc.content {
        write_element_svg(&elem, &mut writer)?;
        write_element_svg(elem.as_ref(), &mut writer)?;
    }

    writer.write_event(quick_xml::events::Event::End(quick_xml::events::BytesEnd::borrowed(b"svg")))?;

M src/types.rs => src/types.rs +6 -0
@@ 23,3 23,9 @@ pub struct AABB {
    pub position: Point2,
    pub size: Size2,
}

impl AABB {
    pub fn contains(&self, point: Point2) -> bool {
        return point.x > self.position.x && point.y > self.position.y && point.x < self.position.x + self.size.width && point.y < self.position.y + self.size.height;
    }
}