basic druid drawing canvas
This commit is contained in:
parent
995c544149
commit
20ffea1dbe
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
110
src/main.rs
110
src/main.rs
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue