// 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);
}
}
}