~vpzom/savg

a5f632c12f9b4aa768b470ae641b6ad4cc450a15 — Colin Reeder 5 years ago 8bd85aa
Scaling
4 files changed, 188 insertions(+), 23 deletions(-)

M src/document.rs
A src/draw.rs
M src/main.rs
M src/types.rs
M src/document.rs => src/document.rs +22 -15
@@ 1,5 1,3 @@
use cairo;

use color;
use types::*;



@@ 45,6 43,12 @@ impl<T> AsRef<T> for Object<T> {
    }
}

impl<T> AsMut<T> for Object<T> {
    fn as_mut(&mut self) -> &mut T {
        &mut self.value
    }
}

pub enum Element {
    Rect(Rect)
}


@@ 58,24 62,18 @@ impl Element {
            }
        }
    }
    pub fn draw(&self, ctx: &cairo::Context) {
    pub fn as_scalable_mut(&mut self) -> Option<&mut ScalableElement> {
        match self {
            Element::Rect(rect) => {
                ctx.rectangle(rect.position.x, rect.position.y, rect.size.width, rect.size.height);
                if let Some(ref fill) = rect.presentation.fill {
                    match fill {
                        Paint::Color(ref color) => match color {
                            color::Color::RGB(rgb) => ctx.set_source_rgb(rgb.r as f64 / 255.0, rgb.g as f64 / 255.0, rgb.b as f64 / 255.0),
                            color::Color::RGBA(rgba) => ctx.set_source_rgba(rgba.r as f64 / 255.0, rgba.g as f64 / 255.0, rgba.b as f64 / 255.0, rgba.a as f64),
                        }
                    }
                    ctx.fill();
                }
            }
            Element::Rect(rect) => Some(rect),
        }
    }
}

pub trait ScalableElement {
    fn get_size(&self) -> &Size2;
    fn set_size(&mut self, size: Size2);
}

#[derive(Clone)]
pub struct Rect {
    pub position: Point2,


@@ 83,6 81,15 @@ pub struct Rect {
    pub presentation: Presentation,
}

impl ScalableElement for Rect {
    fn get_size(&self) -> &Size2 {
        &self.size
    }
    fn set_size(&mut self, size: Size2) {
        self.size = size;
    }
}

#[derive(Clone)]
pub struct Presentation {
    pub fill: Option<Paint>,

A src/draw.rs => src/draw.rs +29 -0
@@ 0,0 1,29 @@
use color;

use document::{Element, Paint};
use {DragState, DrawContext};

use std::borrow::Cow;

pub fn draw_elem(elem: &Element, gr: &cairo::Context, ctx: DrawContext, drag_state: &Option<DragState>) {
    match elem {
        Element::Rect(rect) => {
            let mut size = Cow::Borrowed(&rect.size);
            if let Some(DragState::ScaleBoth { start_pos }) = drag_state {
                let diff = ctx.mouse_pos - start_pos;
                size = Cow::Owned(size.as_ref() + &diff);
            }

            gr.rectangle(rect.position.x, rect.position.y, size.width, size.height);
            if let Some(ref fill) = rect.presentation.fill {
                match fill {
                    Paint::Color(ref color) => match color {
                        color::Color::RGB(rgb) => gr.set_source_rgb(rgb.r as f64 / 255.0, rgb.g as f64 / 255.0, rgb.b as f64 / 255.0),
                        color::Color::RGBA(rgba) => gr.set_source_rgba(rgba.r as f64 / 255.0, rgba.g as f64 / 255.0, rgba.b as f64 / 255.0, rgba.a as f64),
                    }
                }
                gr.fill();
            }
        }
    }
}

M src/main.rs => src/main.rs +115 -8
@@ 7,6 7,7 @@ extern crate rand;

mod color;
mod document;
mod draw;
mod save;
mod types;



@@ 25,6 26,11 @@ fn main() {

    let instance = Arc::new(EditInstance {
        document: RwLock::new(Document::new(Size2::new(400.0, 400.0))),
        drag_state: RwLock::new(None),
        last_mouse_pos: RwLock::new(Point2 {
            x: 0.0,
            y: 0.0,
        }),
        repaint: AtomicBool::new(false),
        selection: RwLock::new(None),
        tool: RwLock::new(ToolMode::Select),


@@ 33,7 39,8 @@ fn main() {
    let area = gtk::DrawingArea::new();
    area.set_events(area.get_events() | (
            gdk::EventMask::POINTER_MOTION_MASK |
            gdk::EventMask::BUTTON_PRESS_MASK
            gdk::EventMask::BUTTON_PRESS_MASK |
            gdk::EventMask::BUTTON_RELEASE_MASK
            ).bits() as i32);

    let toolbar = gtk::Box::new(gtk::Orientation::Vertical, 2);


@@ 109,8 116,22 @@ fn main() {
                ctx.rectangle(0.0, 0.0, document.size.width, document.size.height);
                ctx.fill();

                let drag_state = instance.drag_state.read().unwrap();
                let last_mouse_pos = instance.last_mouse_pos.read().unwrap();

                for elem in &document.content {
                    elem.as_ref().draw(ctx);
                    draw::draw_elem(elem.as_ref(), ctx, DrawContext {
                        mouse_pos: &last_mouse_pos,
                    }, if let Some(selection) = selection {
                        if selection.id() == elem.id() {
                            &drag_state
                        }
                        else {
                            &None
                        }
                    } else {
                        &None
                    });
                }

                if let Some(selection) = selection {


@@ 131,17 152,24 @@ fn main() {
        area.connect_button_press_event(move |_, ev| {
            let (pos_x, pos_y) = ev.get_position();

            if instance.drag_state.read().unwrap().is_some() {
                // already dragging somehow, abort
                return Inhibit(true);
            }

            let tool = instance.tool.read().unwrap();
            let tool: &ToolMode = &tool;
            match tool {
                ToolMode::Rectangle => {
                    let mut document = instance.document.write().unwrap();

                    let position = Point2 {
                        x: pos_x,
                        y: pos_y,
                    };

                    let obj = Object::new_random_id(Element::Rect(Rect {
                        position: Point2 {
                            x: pos_x,
                            y: pos_y,
                        },
                        position: position.clone(),
                        presentation: Presentation {
                            fill: Some(Paint::Color(color::Color::RGB(color::RGB {
                                r: 0,


@@ 150,12 178,15 @@ fn main() {
                            }))),
                        },
                        size: Size2 {
                            width: 100.0,
                            height: 50.0,
                            width: 0.0,
                            height: 0.0,
                        },
                    }));

                    *instance.selection.write().unwrap() = Some(obj.id());
                    *instance.drag_state.write().unwrap() = Some(DragState::ScaleBoth {
                        start_pos: position,
                    });
                    document.content.push(obj);

                    instance.queue_repaint();


@@ 183,6 214,70 @@ fn main() {

    {
        let instance = instance.clone();
        area.connect_button_release_event(move |_, ev| {
            let (pos_x, pos_y) = ev.get_position();

            let mut drag_state = instance.drag_state.write().unwrap();
            let drag_state: &mut Option<DragState> = &mut drag_state;

            if let Some(mode) = drag_state {
                match mode {
                    DragState::ScaleBoth {
                        start_pos,
                    } => {
                        let pos = Point2 {
                            x: pos_x,
                            y: pos_y,
                        };
                        let diff = &pos - start_pos;

                        let selection = *instance.selection.read().unwrap();

                        if let Some(selection) = selection {
                            let mut document = instance.document.write().unwrap();
                            for elem in &mut document.content {
                                if elem.id() == selection {
                                    if let Some(scalable) = elem.as_mut().as_scalable_mut() {
                                        println!("scaling");
                                        let new_size = scalable.get_size() + &diff;
                                        scalable.set_size(new_size);
                                        instance.queue_repaint();
                                    }
                                    else {
                                        eprintln!("scaling non-scalable!");
                                    }
                                    break;
                                }
                            }
                        }
                    },
                }
            }

            *drag_state = None;

            Inhibit(true)
        });
    }

    {
        let instance = instance.clone();
        area.connect_motion_notify_event(move |_, ev| {
            let (x, y) = ev.get_position();
            let mut last_mouse_pos = instance.last_mouse_pos.write().unwrap();
            last_mouse_pos.x = x;
            last_mouse_pos.y = y;

            if instance.drag_state.read().unwrap().is_some() {
                instance.queue_repaint();
            }

            Inhibit(true)
        });
    }

    {
        let instance = instance.clone();
        window.connect_key_press_event(move |_, ev| {
            let key = ev.get_keyval();
            if key == gdk::enums::key::Delete {


@@ 275,6 370,8 @@ struct EditInstance {
    repaint: AtomicBool,
    selection: RwLock<Option<u64>>,
    tool: RwLock<ToolMode>,
    drag_state: RwLock<Option<DragState>>,
    last_mouse_pos: RwLock<Point2>,
}

impl EditInstance {


@@ 288,3 385,13 @@ enum ToolMode {
    Select,
    Rectangle,
}

pub enum DragState {
    ScaleBoth {
        start_pos: Point2
    },
}

pub struct DrawContext<'a> {
    pub mouse_pos: &'a Point2,
}

M src/types.rs => src/types.rs +22 -0
@@ 4,12 4,34 @@ pub struct Point2 {
    pub y: f64,
}

impl<'a> std::ops::Sub for &'a Point2 {
    type Output = Size2;

    fn sub(self, other: &Point2) -> Size2 {
        Size2 {
            width: self.x - other.x,
            height: self.y - other.y,
        }
    }
}

#[derive(Clone)]
pub struct Size2 {
    pub width: f64,
    pub height: f64,
}

impl<'a> std::ops::Add for &'a Size2 {
    type Output = Size2;

    fn add(self, other: &Size2) -> Size2 {
        Size2 {
            width: self.width + other.width,
            height: self.height + other.height,
        }
    }
}

impl Size2 {
    pub fn new(width: f64, height: f64) -> Self {
        Size2 {