// 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 im::Vector; use super::tool_ctx::{CanvasToolCtx}; use crate::canvas::Canvas; use crate::{DocumentSnapshot, VersionedCanvas}; use druid::widget::prelude::*; use druid::{Color, Data, Env, Event, PointerType}; #[derive(Clone, Data)] pub struct CanvasState { versioned_canvas: VersionedCanvas, tool_ctx: CanvasToolCtx, temporary_erasing: bool, } impl CanvasState { pub fn new(tool_ctx: CanvasToolCtx) -> Self { CanvasState { versioned_canvas: VersionedCanvas::new(Canvas::new()), tool_ctx: tool_ctx, temporary_erasing: true, } } pub fn perform_undo(&mut self) { //if !self.is_drawing() { self.versioned_canvas.undo(); //} } pub fn perform_redo(&mut self) { //if !self.is_drawing() { self.versioned_canvas.redo(); //} } pub fn get_document_snapshot(&self) -> DocumentSnapshot { DocumentSnapshot { format_version_major: 0, format_version_minor: 1, canvas_elements: self .versioned_canvas .get() .elements() .iter() .cloned() .collect(), } } pub fn set_from_snapshot(&mut self, snapshot: DocumentSnapshot) { self.versioned_canvas = VersionedCanvas::new(Canvas::new_with_elements(Vector::from( snapshot.canvas_elements, ))); } pub fn set_tool_ctx(&mut self, ctx: CanvasToolCtx) { self.tool_ctx = ctx; } pub fn get_tool_ctx(&self) -> &CanvasToolCtx { &self.tool_ctx } pub fn get_tool_ctx_mut(&mut self) -> &mut CanvasToolCtx { &mut self.tool_ctx } pub fn handle_event(&mut self, mut ctx: &mut EventCtx, event: &Event, env: &Env) { self.tool_ctx .handle_event(ctx, event, &mut self.versioned_canvas, env); } } pub struct CanvasWidget; impl Widget for CanvasWidget { fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut CanvasState, env: &Env) { ctx.request_focus(); let mut toggle_eraser_event = false; let mut enable_temporary_erasing = false; match event { Event::MouseDown(mouse_event) => { toggle_eraser_event = true; enable_temporary_erasing = mouse_event.pointer_type == PointerType::Eraser; } Event::MouseMove(mouse_event) => { toggle_eraser_event = true; enable_temporary_erasing = mouse_event.pointer_type == PointerType::Eraser; } Event::MouseUp(mouse_event) => { toggle_eraser_event = true; enable_temporary_erasing = mouse_event.pointer_type == PointerType::Eraser; } _ => {} } // TODO: the first eraser toggle is not handled if toggle_eraser_event && data.temporary_erasing != enable_temporary_erasing { if enable_temporary_erasing { ctx.submit_notification(crate::commands::PUSH_ERASER); } else { ctx.submit_notification(crate::commands::POP_ERASER); } data.temporary_erasing = enable_temporary_erasing; } else { data.handle_event(ctx, event, env); } } fn lifecycle( &mut self, _ctx: &mut LifeCycleCtx, _event: &LifeCycle, _data: &CanvasState, _env: &Env, ) { } fn update( &mut self, ctx: &mut UpdateCtx, _old_data: &CanvasState, _data: &CanvasState, _env: &Env, ) { // the current_element is moved to the versioned_canvas array, no need to repaint //if old_data.is_drawing() && !data.is_drawing() { // return; //} //if data.is_drawing() { // if let Some(e) = data.current_element.as_ref() { // ctx.request_paint_rect(e.bounding_box()); // } //} else { ctx.request_paint(); //} } fn layout( &mut self, _layout_ctx: &mut LayoutCtx, bc: &BoxConstraints, _data: &CanvasState, _env: &Env, ) -> Size { // BoxConstraints are passed by the parent widget. // This method can return any Size within those constraints: // bc.constrain(my_size) // // To check if a dimension is infinite or not (e.g. scrolling): // bc.is_width_bounded() / bc.is_height_bounded() bc.max() } // The paint method gets called last, after an event flow. // It goes event -> update -> layout -> paint, and each method can influence the next. // Basically, anything that changes the appearance of a widget causes a paint. fn paint(&mut self, ctx: &mut PaintCtx, data: &CanvasState, env: &Env) { // (ctx.size() returns the size of the layout rect we're painting in) let size = ctx.size(); let rect = size.to_rect(); ctx.fill(rect, &Color::WHITE); for element in data.versioned_canvas.get().elements().iter() { element.draw(ctx); } if data.tool_ctx.needs_repaint() { data.tool_ctx.paint(ctx, env); } } }