240 lines · 8.8 KB · Rust
RawDownload
use gpui::{    canvas, div, linear_color_stop, linear_gradient, point, prelude::*, px, rgb, size, Application,    Background, Bounds, ColorSpace, Context, MouseDownEvent, Path, PathBuilder, PathStyle, Pixels,    Point, Render, StrokeOptions, Window, WindowOptions,}; struct PaintingViewer {    default_lines: Vec<(Path<Pixels>, Background)>,    lines: Vec<Vec<Point<Pixels>>>,    start: Point<Pixels>,    _painting: bool,} impl PaintingViewer {    fn new(_window: &mut Window, _cx: &mut Context<Self>) -> Self {        let mut lines = vec![];         // draw a Rust logo        let mut builder = lyon::path::Path::svg_builder();        lyon::extra::rust_logo::build_logo_path(&mut builder);        // move down the Path        let mut builder: PathBuilder = builder.into();        builder.translate(point(px(10.), px(100.)));        builder.scale(0.9);        let path = builder.build().unwrap();        lines.push((path, gpui::black().into()));         // draw a lightening bolt ⚡        let mut builder = PathBuilder::fill();        builder.move_to(point(px(150.), px(200.)));        builder.line_to(point(px(200.), px(125.)));        builder.line_to(point(px(200.), px(175.)));        builder.line_to(point(px(250.), px(100.)));        let path = builder.build().unwrap();        lines.push((path, rgb(0x1d4ed8).into()));         // draw a ⭐        let mut builder = PathBuilder::fill();        builder.move_to(point(px(350.), px(100.)));        builder.line_to(point(px(370.), px(160.)));        builder.line_to(point(px(430.), px(160.)));        builder.line_to(point(px(380.), px(200.)));        builder.line_to(point(px(400.), px(260.)));        builder.line_to(point(px(350.), px(220.)));        builder.line_to(point(px(300.), px(260.)));        builder.line_to(point(px(320.), px(200.)));        builder.line_to(point(px(270.), px(160.)));        builder.line_to(point(px(330.), px(160.)));        builder.line_to(point(px(350.), px(100.)));        let path = builder.build().unwrap();        lines.push((            path,            linear_gradient(                180.,                linear_color_stop(rgb(0xFACC15), 0.7),                linear_color_stop(rgb(0xD56D0C), 1.),            )            .color_space(ColorSpace::Oklab),        ));         let square_bounds = Bounds {            origin: point(px(450.), px(100.)),            size: size(px(200.), px(80.)),        };        let height = square_bounds.size.height;        let horizontal_offset = height;        let vertical_offset = px(30.);        let mut builder = PathBuilder::fill();        builder.move_to(square_bounds.bottom_left());        builder.curve_to(            square_bounds.origin + point(horizontal_offset, vertical_offset),            square_bounds.origin + point(px(0.0), vertical_offset),        );        builder.line_to(square_bounds.top_right() + point(-horizontal_offset, vertical_offset));        builder.curve_to(            square_bounds.bottom_right(),            square_bounds.top_right() + point(px(0.0), vertical_offset),        );        builder.line_to(square_bounds.bottom_left());        let path = builder.build().unwrap();        lines.push((            path,            linear_gradient(                180.,                linear_color_stop(gpui::blue(), 0.4),                linear_color_stop(gpui::red(), 1.),            ),        ));         // draw a wave        let options = StrokeOptions::default()            .with_line_width(1.)            .with_line_join(lyon::path::LineJoin::Bevel);        let mut builder = PathBuilder::stroke(px(1.)).with_style(PathStyle::Stroke(options));        builder.move_to(point(px(40.), px(320.)));        for i in 0..50 {            builder.line_to(point(                px(40.0 + i as f32 * 10.0),                px(320.0 + (i as f32 * 10.0).sin() * 40.0),            ));        }        let path = builder.build().unwrap();        lines.push((path, gpui::green().into()));         Self {            default_lines: lines.clone(),            lines: vec![],            start: point(px(0.), px(0.)),            _painting: false,        }    }     fn clear(&mut self, cx: &mut Context<Self>) {        self.lines.clear();        cx.notify();    }}impl Render for PaintingViewer {    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {        let default_lines = self.default_lines.clone();        let lines = self.lines.clone();        div()            .font_family(".SystemUIFont")            .bg(gpui::white())            .size_full()            .p_4()            .flex()            .flex_col()            .child(                div()                    .flex()                    .gap_2()                    .justify_between()                    .items_center()                    .child("Mouse down any point and drag to draw lines (Hold on shift key to draw straight lines)")                    .child(                        div()                            .id("clear")                            .child("Clean up")                            .bg(gpui::black())                            .text_color(gpui::white())                            .active(|this| this.opacity(0.8))                            .flex()                            .px_3()                            .py_1()                            .on_click(cx.listener(|this, _, _, cx| {                                this.clear(cx);                            })),                    ),            )            .child(                div()                    .size_full()                    .child(                        canvas(                            move |_, _, _| {},                            move |_, _, window, _| {                                 for (path, color) in default_lines {                                    window.paint_path(path, color);                                }                                 for points in lines {                                    if points.len() < 2 {                                        continue;                                    }                                     let mut builder = PathBuilder::stroke(px(1.));                                    for (i, p) in points.into_iter().enumerate() {                                        if i == 0 {                                            builder.move_to(p);                                        } else {                                            builder.line_to(p);                                        }                                    }                                     if let Ok(path) = builder.build() {                                        window.paint_path(path, gpui::black());                                    }                                }                            },                        )                        .size_full(),                    )                    .on_mouse_down(                        gpui::MouseButton::Left,                        cx.listener(|this, ev: &MouseDownEvent, _, _| {                            this._painting = true;                            this.start = ev.position;                            let path = vec![ev.position];                            this.lines.push(path);                        }),                    )                    .on_mouse_move(cx.listener(|this, ev: &gpui::MouseMoveEvent, _, cx| {                        if !this._painting {                            return;                        }                         let is_shifted = ev.modifiers.shift;                        let mut pos = ev.position;                        // When holding shift, draw a straight line                        if is_shifted {                            let dx = pos.x - this.start.x;                            let dy = pos.y - this.start.y;                            if dx.abs() > dy.abs() {                                pos.y = this.start.y;                            } else {                                pos.x = this.start.x;                            }                        }                         if let Some(path) = this.lines.last_mut() {                            path.push(pos);                        }                         cx.notify();                    }))                    .on_mouse_up(                        gpui::MouseButton::Left,                        cx.listener(|this, _, _, _| {                            this._painting = false;                        }),                    ),            )    }} fn main() {    Application::new().run(|cx| {        cx.open_window(            WindowOptions {                focus: true,                ..Default::default()            },            |window, cx| cx.new(|cx| PaintingViewer::new(window, cx)),        )        .unwrap();        cx.activate(true);    });}