basic druid drawing canvas

This commit is contained in:
Enrico Lumetti 2020-10-16 13:54:10 +02:00
parent 995c544149
commit 20ffea1dbe
3 changed files with 106 additions and 63 deletions

56
Cargo.lock generated
View File

@ -67,6 +67,15 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "bitmaps"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2"
dependencies = [
"typenum",
]
[[package]] [[package]]
name = "block" name = "block"
version = "0.1.6" version = "0.1.6"
@ -275,6 +284,7 @@ dependencies = [
"fluent-langneg", "fluent-langneg",
"fluent-syntax", "fluent-syntax",
"fnv", "fnv",
"im",
"instant", "instant",
"log", "log",
"simple_logger", "simple_logger",
@ -748,6 +758,20 @@ dependencies = [
"libc", "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]] [[package]]
name = "input" name = "input"
version = "0.5.0" version = "0.5.0"
@ -1113,6 +1137,21 @@ dependencies = [
"proc-macro2", "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]] [[package]]
name = "regex-automata" name = "regex-automata"
version = "0.1.9" version = "0.1.9"
@ -1224,6 +1263,16 @@ dependencies = [
"winapi", "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]] [[package]]
name = "slab" name = "slab"
version = "0.4.2" version = "0.4.2"
@ -1310,6 +1359,7 @@ dependencies = [
"gdk", "gdk",
"gio", "gio",
"gtk", "gtk",
"im",
"input", "input",
"libc", "libc",
"udev 0.2.0", "udev 0.2.0",
@ -1441,6 +1491,12 @@ dependencies = [
"fxhash", "fxhash",
] ]
[[package]]
name = "typenum"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
[[package]] [[package]]
name = "udev" name = "udev"
version = "0.2.0" version = "0.2.0"

View File

@ -12,7 +12,8 @@ udev = "0.2"
libc = "0.2" libc = "0.2"
errno = "0.2" errno = "0.2"
csv = "1.1" 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] [dependencies.gtk]
version = "0.9.2" version = "0.9.2"

View File

@ -14,37 +14,67 @@
//! An example of a custom drawing widget. //! An example of a custom drawing widget.
use druid::im::{vector, Vector};
use druid::kurbo::BezPath; use druid::kurbo::BezPath;
use druid::piet::{FontFamily, ImageFormat, InterpolationMode}; use druid::piet::{FontFamily, ImageFormat, InterpolationMode};
use druid::widget::prelude::*; use druid::widget::prelude::*;
use druid::{ use druid::{
Affine, AppLauncher, ArcStr, Color, FontDescriptor, LocalizedString, Point, Rect, TextLayout, Affine, AppLauncher, ArcStr, Color, Data, Event, FontDescriptor, LocalizedString, Point, Rect, TextLayout,
WindowDesc, WindowDesc,
}; };
struct CustomWidget; #[derive(Clone, Data)]
struct CanvasData {
drawn_paths: Vector<BezPath>,
is_drawing: bool,
}
impl Widget<String> for CustomWidget { struct CanvasWidget;
fn event(&mut self, _ctx: &mut EventCtx, event: &Event, _data: &mut String, _env: &Env) {
println!("{:?}", event); impl Widget<CanvasData> 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( fn lifecycle(
&mut self, &mut self,
_ctx: &mut LifeCycleCtx, _ctx: &mut LifeCycleCtx,
_event: &LifeCycle, _event: &LifeCycle,
_data: &String, _data: &CanvasData,
_env: &Env, _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( fn layout(
&mut self, &mut self,
_layout_ctx: &mut LayoutCtx, _layout_ctx: &mut LayoutCtx,
bc: &BoxConstraints, bc: &BoxConstraints,
_data: &String, _data: &CanvasData,
_env: &Env, _env: &Env,
) -> Size { ) -> Size {
// BoxConstraints are passed by the parent widget. // BoxConstraints are passed by the parent widget.
@ -59,7 +89,7 @@ impl Widget<String> for CustomWidget {
// The paint method gets called last, after an event flow. // The paint method gets called last, after an event flow.
// It goes event -> update -> layout -> paint, and each method can influence the next. // 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. // 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! // Let's draw a picture with Piet!
// Clear the whole widget with the color of your choice // Clear the whole widget with the color of your choice
@ -71,67 +101,23 @@ impl Widget<String> for CustomWidget {
// Note: ctx also has a `clear` method, but that clears the whole context, // Note: ctx also has a `clear` method, but that clears the whole context,
// and we only want to clear this widget's area. // 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); let stroke_color = Color::rgb8(0, 128, 0);
// Stroke the path with thickness 1.0 for path in &data.drawn_paths {
ctx.stroke(path, &stroke_color, 1.0); ctx.stroke(path, &stroke_color, 2.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::<ArcStr>::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);
} }
} }
pub fn main() { 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"), 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) AppLauncher::with_window(window)
.use_simple_logger() .use_simple_logger()
.launch("Druid + Piet".to_string()) .launch(canvas_data)
.expect("launch failed"); .expect("launch failed");
} }
fn make_image_data(width: usize, height: usize) -> Vec<u8> {
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
}