diff --git a/Cargo.lock b/Cargo.lock index 929e373..36e5a6f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,6 +67,15 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "bitmaps" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" +dependencies = [ + "typenum", +] + [[package]] name = "block" version = "0.1.6" @@ -275,6 +284,7 @@ dependencies = [ "fluent-langneg", "fluent-syntax", "fnv", + "im", "instant", "log", "simple_logger", @@ -748,6 +758,20 @@ dependencies = [ "libc", ] +[[package]] +name = "im" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "111c1983f3c5bb72732df25cddacee9b546d08325fb584b5ebd38148be7b0246" +dependencies = [ + "bitmaps", + "rand_core", + "rand_xoshiro", + "sized-chunks", + "typenum", + "version_check", +] + [[package]] name = "input" version = "0.5.0" @@ -1113,6 +1137,21 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" + +[[package]] +name = "rand_xoshiro" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9fcdd2e881d02f1d9390ae47ad8e5696a9e4be7b547a1da2afbc61973217004" +dependencies = [ + "rand_core", +] + [[package]] name = "regex-automata" version = "0.1.9" @@ -1224,6 +1263,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "sized-chunks" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec31ceca5644fa6d444cc77548b88b67f46db6f7c71683b0f9336e671830d2f" +dependencies = [ + "bitmaps", + "typenum", +] + [[package]] name = "slab" version = "0.4.2" @@ -1310,6 +1359,7 @@ dependencies = [ "gdk", "gio", "gtk", + "im", "input", "libc", "udev 0.2.0", @@ -1441,6 +1491,12 @@ dependencies = [ "fxhash", ] +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + [[package]] name = "udev" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 8161db0..ee4798f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,8 @@ udev = "0.2" libc = "0.2" errno = "0.2" csv = "1.1" -druid = { git = "https://github.com/doppioandante/druid", branch = "stylus_events" } +druid = { git = "https://github.com/doppioandante/druid", branch = "stylus_events", features = ["im"] } +im = { version = "*" } [dependencies.gtk] version = "0.9.2" diff --git a/src/main.rs b/src/main.rs index be64506..c394f48 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,37 +14,67 @@ //! An example of a custom drawing widget. +use druid::im::{vector, Vector}; use druid::kurbo::BezPath; use druid::piet::{FontFamily, ImageFormat, InterpolationMode}; use druid::widget::prelude::*; use druid::{ - Affine, AppLauncher, ArcStr, Color, FontDescriptor, LocalizedString, Point, Rect, TextLayout, + Affine, AppLauncher, ArcStr, Color, Data, Event, FontDescriptor, LocalizedString, Point, Rect, TextLayout, WindowDesc, }; -struct CustomWidget; +#[derive(Clone, Data)] +struct CanvasData { + drawn_paths: Vector, + is_drawing: bool, +} -impl Widget for CustomWidget { - fn event(&mut self, _ctx: &mut EventCtx, event: &Event, _data: &mut String, _env: &Env) { - println!("{:?}", event); +struct CanvasWidget; + +impl Widget for CanvasWidget { + fn event(&mut self, _ctx: &mut EventCtx, event: &Event, data: &mut CanvasData, _env: &Env) { + match event { + Event::MouseDown(mouse_event) => { + if mouse_event.pointer_type == druid::PointerType::Stylus { + data.is_drawing = true; + let mut new_path = BezPath::new(); + new_path.move_to((mouse_event.pos.x, mouse_event.pos.y)); + data.drawn_paths.push_back(new_path); + } + }, + Event::MouseMove(mouse_event) => { + if data.is_drawing && mouse_event.pointer_type == druid::PointerType::Stylus { + data.drawn_paths.back_mut().unwrap().line_to((mouse_event.pos.x, mouse_event.pos.y)); + } + }, + Event::MouseUp(_) => { + if data.is_drawing { + data.is_drawing = false; + } + }, + _ => {} + + } } fn lifecycle( &mut self, _ctx: &mut LifeCycleCtx, _event: &LifeCycle, - _data: &String, + _data: &CanvasData, _env: &Env, ) { } - fn update(&mut self, _ctx: &mut UpdateCtx, _old_data: &String, _data: &String, _env: &Env) {} + fn update(&mut self, ctx: &mut UpdateCtx, _old_data: &CanvasData, _data: &CanvasData, _env: &Env) { + ctx.request_paint(); + } fn layout( &mut self, _layout_ctx: &mut LayoutCtx, bc: &BoxConstraints, - _data: &String, + _data: &CanvasData, _env: &Env, ) -> Size { // BoxConstraints are passed by the parent widget. @@ -59,7 +89,7 @@ impl Widget for CustomWidget { // 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: &String, env: &Env) { + fn paint(&mut self, ctx: &mut PaintCtx, data: &CanvasData, _env: &Env) { // Let's draw a picture with Piet! // Clear the whole widget with the color of your choice @@ -71,67 +101,23 @@ impl Widget for CustomWidget { // Note: ctx also has a `clear` method, but that clears the whole context, // and we only want to clear this widget's area. - // Create an arbitrary bezier path - let mut path = BezPath::new(); - path.move_to(Point::ORIGIN); - path.quad_to((80.0, 90.0), (size.width, size.height)); - // Create a color let stroke_color = Color::rgb8(0, 128, 0); - // Stroke the path with thickness 1.0 - ctx.stroke(path, &stroke_color, 1.0); - - // Rectangles: the path for practical people - let rect = Rect::from_origin_size((10., 10.), (100., 100.)); - // Note the Color:rgba8 which includes an alpha channel (7F in this case) - let fill_color = Color::rgba8(0x00, 0x00, 0x00, 0x7F); - ctx.fill(rect, &fill_color); - - // Text is easy; in real use TextLayout should be stored in the widget - // and reused. - let mut layout = TextLayout::::from_text(data.to_owned()); - layout.set_font(FontDescriptor::new(FontFamily::SERIF).with_size(24.0)); - layout.set_text_color(fill_color); - layout.rebuild_if_needed(ctx.text(), env); - - // Let's rotate our text slightly. First we save our current (default) context: - ctx.with_save(|ctx| { - // Now we can rotate the context (or set a clip path, for instance): - ctx.transform(Affine::rotate(0.1)); - layout.draw(ctx, (80.0, 40.0)); - }); - // When we exit with_save, the original context's rotation is restored - - // Let's burn some CPU to make a (partially transparent) image buffer - let image_data = make_image_data(256, 256); - let image = ctx - .make_image(256, 256, &image_data, ImageFormat::RgbaSeparate) - .unwrap(); - // The image is automatically scaled to fit the rect you pass to draw_image - ctx.draw_image(&image, size.to_rect(), InterpolationMode::Bilinear); + for path in &data.drawn_paths { + ctx.stroke(path, &stroke_color, 2.0); + } } } pub fn main() { - let window = WindowDesc::new(|| CustomWidget {}).title( + let window = WindowDesc::new(|| CanvasWidget {}).title( LocalizedString::new("custom-widget-demo-window-title").with_placeholder("Fancy Colors"), ); + let canvas_data = CanvasData { + drawn_paths: vector![], + is_drawing: false, + }; AppLauncher::with_window(window) .use_simple_logger() - .launch("Druid + Piet".to_string()) + .launch(canvas_data) .expect("launch failed"); } - -fn make_image_data(width: usize, height: usize) -> Vec { - let mut result = vec![0; width * height * 4]; - for y in 0..height { - for x in 0..width { - let ix = (y * width + x) * 4; - result[ix] = x as u8; - result[ix + 1] = y as u8; - result[ix + 2] = !(x as u8); - result[ix + 3] = 127; - } - } - result -} -