前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Rust Druid 之自绘窗体

Rust Druid 之自绘窗体

原创
作者头像
8菠萝
修改2021-05-26 09:59:24
2K0
修改2021-05-26 09:59:24
举报
文章被收录于专栏:菠萝上市没有菠萝上市没有

概念

渲染

Druid 底层的窗体渲染使用 piet 库,piet库封装了各平台的GDI 接口,对上层屏蔽每个平台的具体实现。

在Druid中,只要关注它的 PaintCtx 结构与 RenderContext 接口。

context.rs

代码语言:javascript
复制
pub struct PaintCtx<'a, 'b, 'c> {
    pub(crate) state: &'a mut ContextState<'b>,
    pub(crate) widget_state: &'a WidgetState,
    // 实际渲染使用了 Piet
    pub render_ctx: &'a mut Piet<'c>,
    // 绘图的顺序
    pub(crate) z_ops: Vec<ZOrderPaintOp>,
    // 可视区域
    pub(crate) region: Region,
    // 深度
    pub(crate) depth: u32,
}

render_context.rs

代码语言:javascript
复制
// 删减了部分代码
pub trait RenderContext {
    // 状态
    fn status(&mut self) -> Result<(), Error>;
    // 笔刷
    fn solid_brush(&mut self, color: Color) -> Self::Brush;
    // 渐变
    fn gradient(&mut self, gradient: impl Into<FixedGradient>) -> Result<Self::Brush, Error>;
    // 使用指定颜色
    fn clear(&mut self, region: impl Into<Option<Rect>>, color: Color);
  	// 线条
    fn stroke(&mut self, shape: impl Shape, brush: &impl IntoBrush<Self>, width: f64);
   	// 线条样式
    fn stroke_styled(
        &mut self,
        shape: impl Shape,
        brush: &impl IntoBrush<Self>,
        width: f64,
        style: &StrokeStyle,
    );
	// 使用non-zero规则填充
    fn fill(&mut self, shape: impl Shape, brush: &impl IntoBrush<Self>);
	// 使用even-odd规则填充
    fn fill_even_odd(&mut self, shape: impl Shape, brush: &impl IntoBrush<Self>);
    // 遮蔽
    fn clip(&mut self, shape: impl Shape);
   	// 文本
    fn text(&mut self) -> &mut Self::Text;
    // 绘制文本
    fn draw_text(&mut self, layout: &Self::TextLayout, pos: impl Into<Point>);
    // 保存状态
    fn save(&mut self) -> Result<(), Error>;
    // 恢复状态
    fn restore(&mut self) -> Result<(), Error>;
   	// 略
    fn with_save(&mut self, f: impl FnOnce(&mut Self);
    // 略
    fn finish(&mut self) -> Result<(), Error>;
    // 矩阵变化
    fn transform(&mut self, transform: Affine);
  	// ... 等
}

上面定义的绘图接口与Canvas的绘图接口非常类似,通过上门的接口, 我们可用实现自定义的一些控件。

图形

Render 接口中需要指定 impl Shape. Druid 中的2D图形相关定义都在 druid::piet::kurbo 中定义。

具体图形有(翻译了一部分, 具体文档上有详细说明):

Affine

A 2D affine transform.

Arc

曲线段

BezPath

贝塞尔曲线

Circle

圆形

CircleSegment

圆弧

ConstPoint

A trivial "curve" that is just a constant.

CubicBez

A single cubic Bézier segment.

CubicBezIter

An iterator for cubic beziers.

Ellipse

椭圆

Insets

Insets from the edges of a rectangle.

Line

线

LineIntersection

An intersection of a Line and a PathSeg.

PathSegIter

An iterator for path segments.

Point

QuadBez

A single quadratic Bézier segment.

QuadBezIter

An iterator for quadratic beziers.

Rect

矩形

RoundedRect

A rectangle with equally rounded corners.

Segments

An iterator that transforms path elements to path segments.

Size

A 2D size.

SvgArc

A single SVG arc segment.

TranslateScale

A transformation including scaling and translation.

Vec2

A 2D vector.

实践

流程:

  1. 定义一个自绘Widget
  2. 在paint函数中实现绘制效果
代码语言:javascript
复制
use druid::{piet::StrokeStyle, widget::prelude::*};
use druid::{
    kurbo::{Circle, Line},
    FontDescriptor, FontFamily, Rect, TextLayout,
};
use druid::{AppLauncher, Color, WindowDesc};

#[derive(Clone, Data)]
struct DrawWidget {
    data: f64,
}

impl Widget<String> for DrawWidget {
    fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut String, env: &Env) {
    }

    fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &String, env: &Env) {
    }

    fn update(&mut self, ctx: &mut UpdateCtx, old_data: &String, data: &String, env: &Env) {
    }

    fn layout(
        &mut self,
        ctx: &mut LayoutCtx,
        bc: &BoxConstraints,
        data: &String,
        env: &Env,
    ) -> Size {
        bc.constrain((200.0, 200.0))
    }

    fn paint(&mut self, ctx: &mut PaintCtx, data: &String, env: &Env) {
        let size = Size::new(40.0, 40.0);
        // 矩形
        let clip_rect = size.to_rect().inset(-1.0).to_rounded_rect(3.0);

        ctx.stroke(clip_rect, &Color::GREEN, 2.0);

        // 圆形
        let circle = Circle::new((60.0, 20.0), 20.0);
        ctx.fill(circle, &Color::YELLOW);

        // 椭圆
        let rect = Rect::from_origin_size((80., 0.), (80., 40.));
        let path = rect.to_ellipse();
        ctx.fill(path, &Color::MAROON);

        // 线
        let path = Line::new((0., 50.),  (320., 50.));
        let mut stroke_style = StrokeStyle::new();
        stroke_style.set_dash_offset(2.0);
        ctx.stroke_styled(path, &Color::TEAL, 2.0,  &stroke_style);

        // 文本
        let mut text = TextLayout::new();
        text.set_text(data.to_string());
        text.set_font(FontDescriptor::new(FontFamily::default()).with_size(24.0));
        text.set_text_color(Color::WHITE);
        // 涉及到Text属性修改时, 需要调用rebuild方法
        text.rebuild_if_needed(ctx.text(), env);
        text.draw(ctx, (180., 0.));

        // 窗体边框颜色
        let border_size = ctx.size();
        let border_rect = border_size.to_rect().inset(-1.0).to_rounded_rect(2.0);
        ctx.stroke(border_rect, &Color::RED, 2.0);
    }
}

pub fn main() {
    let window = WindowDesc::new(DrawWidget { data: 0.0 }).window_size((400., 400.));

    AppLauncher::with_window(window)
        .log_to_console()
        .launch("Hello Druid ;)".to_string())
        .unwrap();
}        

运行效果
运行效果

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概念
    • 渲染
      • 图形
      • 实践
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档