iced源码分析

前言

iced是一个比较流行的UI库,设计思路还是挺有意思的,不过因为rust复杂的语法,这个库确实很难让一个不精通rust的开发者那么容易理解。这里记录下这几天的阅读源码心得。

正文

iced核心包括四个模块。

  1. iced库,主要控制应用状态、核心是application。
  2. iced core这是真正的iced的核心库,主要功能包括三部分
  1. 通过winit创建窗口,以及处理窗口的点击事件
  2. 在窗口回调杨中,使用widget库绘制图像
  3. 而widget的绘制则是通过tiny_skia或者wgup提供的renderer绘制图像。
  1. tiny_skia或者wgup提提供的renderer绘制图像
  2. winit提供窗口管理

所以iced_core才是真的库核心。管理所有业务逻辑,而iced源码基本上只是提供用户最基本的基本调用

下面跟踪一下applaction的构造过程。我们参考一下计数器的实例代码,分析一下构造过程。


pub fn main() -> iced::Result {iced::run("A cool counter", Counter::update, Counter::view)
}#[derive(Default)]
struct Counter {value: i64,
}#[derive(Debug, Clone, Copy)]
enum Message {Increment,Decrement,
}impl Counter {fn update(&mut self, message: Message) {match message {Message::Increment => {self.value += 1;}Message::Decrement => {self.value -= 1;}}}fn view(&self) -> Column<Message> {column![button("Increment").on_press(Message::Increment),text(self.value).size(50),button("Decrement").on_press(Message::Decrement)].padding(20).align_x(Center)}
}t

iced::run提供静态函数,用于创建applaction。便于用户使用具体构造函数我们看下

pub fn run<State, Message, Theme, Renderer>(title: impl application::Title<State> + 'static,update: impl application::Update<State, Message> + 'static,view: impl for<'a> application::View<'a, State, Message, Theme, Renderer>+ 'static,
) -> Result
whereState: Default + 'static,Message: std::fmt::Debug + Send + 'static,Theme: Default + program::DefaultStyle + 'static,Renderer: program::Renderer + 'static,
{application(title, update, view).run()
}

这段代码主要是str实现了titile接口,Fn实现了Update。Fn实现了View接口。

impl<State> Title<State> for &'static str {fn title(&self, _state: &State) -> String {self.to_string()}
}impl<T, State, Message, C> Update<State, Message> for T
whereT: Fn(&mut State, Message) -> C,C: Into<Task<Message>>,
{fn update(&self,state: &mut State,message: Message,) -> impl Into<Task<Message>> {self(state, message)}
}impl<'a, T, State, Message, Theme, Renderer, Widget>View<'a, State, Message, Theme, Renderer> for T
whereT: Fn(&'a State) -> Widget,State: 'static,Widget: Into<Element<'a, Message, Theme, Renderer>>,
{fn view(&self,state: &'a State,) -> impl Into<Element<'a, Message, Theme, Renderer>> {self(state)}
}

关于返回值,因为Element都为每种widget实现了From接口,所以都会自动转化位Element。返回值是编译器自动转化。
最终我们得到了Applaction。

pub fn application<State, Message, Theme, Renderer>(title: impl Title<State>,update: impl Update<State, Message>,view: impl for<'a> self::View<'a, State, Message, Theme, Renderer>,
) -> Application<impl Program<State = State, Message = Message, Theme = Theme>>
whereState: 'static,Message: Send + std::fmt::Debug + 'static,Theme: Default + DefaultStyle,Renderer: program::Renderer,
{......
}

注意这里的Theme是通过外层widget确认的。而renderer却比较奇葩,这是一个非常奇怪的东西。他的根trait是core模块的render.rs 定义的。在不同的绘制的目的,有不同的嘴trait。比如core目录下的image.rs 下的renderer

pub trait Renderer: crate::Renderer {/// The image Handle to be displayed. Iced exposes its own default implementation of a [`Handle`]////// [`Handle`]: Self::Handletype Handle: Clone;/// Returns the dimensions of an image for the given [`Handle`].fn measure_image(&self, handle: &Self::Handle) -> Size<u32>;/// Draws an [`Image`] inside the provided `bounds`.fn draw_image(&mut self, image: Image<Self::Handle>, bounds: Rectangle);
}

最终是在不同绘制库,有不同的具体实现。比如tiny_skia。则实现了image的特殊绘制接口,比如:

impl core::image::Renderer for Renderer {type Handle = core::image::Handle;fn measure_image(&self, handle: &Self::Handle) -> crate::core::Size<u32> {self.engine.raster_pipeline.dimensions(handle)}fn draw_image(&mut self, image: core::Image, bounds: Rectangle) {let (layer, transformation) = self.layers.current_mut();layer.draw_raster(image, bounds, transformation);}
}

下面开始分析一下程序运行的流程。
applaction是一个包装,核心是 raw: 的program。

    struct Instance<State, Message, Theme, Renderer, Update, View> {update: Update,view: View,_state: PhantomData<State>,_message: PhantomData<Message>,_theme: PhantomData<Theme>,_renderer: PhantomData<Renderer>,}

这正run的函数是:

   fn run(self,settings: Settings,window_settings: Option<window::Settings>,) -> ResultwhereSelf: 'static,Self::State: Default,{self.run_with(settings, window_settings, || {(Self::State::default(), Task::none())})}/// Runs the [`Program`] with the given [`Settings`] and a closure that creates the initial state.fn run_with<I>(self,settings: Settings,window_settings: Option<window::Settings>,initialize: I,) -> ResultwhereSelf: 'static,I: FnOnce() -> (Self::State, Task<Self::Message>) + 'static,{use std::marker::PhantomData;struct Instance<P: Program, I> {program: P,state: P::State,_initialize: PhantomData<I>,}impl<P: Program, I: FnOnce() -> (P::State, Task<P::Message>)>shell::Program for Instance<P, I>{type Message = P::Message;type Theme = P::Theme;type Renderer = P::Renderer;type Flags = (P, I);type Executor = P::Executor;fn new((program, initialize): Self::Flags,) -> (Self, Task<Self::Message>) {let (state, task) = initialize();(Self {program,state,_initialize: PhantomData,},task,)}fn title(&self, window: window::Id) -> String {self.program.title(&self.state, window)}fn update(&mut self,message: Self::Message,) -> Task<Self::Message> {self.program.update(&mut self.state, message)}fn view(&self,window: window::Id,) -> crate::Element<'_, Self::Message, Self::Theme, Self::Renderer>{self.program.view(&self.state, window)}fn subscription(&self) -> Subscription<Self::Message> {self.program.subscription(&self.state)}fn theme(&self, window: window::Id) -> Self::Theme {self.program.theme(&self.state, window)}fn style(&self, theme: &Self::Theme) -> Appearance {self.program.style(&self.state, theme)}fn scale_factor(&self, window: window::Id) -> f64 {self.program.scale_factor(&self.state, window)}}#[allow(clippy::needless_update)]let renderer_settings = crate::graphics::Settings {default_font: settings.default_font,default_text_size: settings.default_text_size,antialiasing: if settings.antialiasing {Some(crate::graphics::Antialiasing::MSAAx4)} else {None},..crate::graphics::Settings::default()};Ok(shell::program::run::<Instance<Self, I>,<Self::Renderer as compositor::Default>::Compositor,>(Settings {id: settings.id,fonts: settings.fonts,default_font: settings.default_font,default_text_size: settings.default_text_size,antialiasing: settings.antialiasing,}.into(),renderer_settings,window_settings,(self, initialize),)?)}

注意这里是program是在iced目录下。这里核心是调用winit的program,最关键的参数是(self, initialize)这个元组。这个参数是构成winit的program。

pub fn run<P, C>(settings: Settings,graphics_settings: graphics::Settings,window_settings: Option<window::Settings>,flags: P::Flags,
) -> Result<(), Error>whereP: Program + 'static,C: Compositor<Renderer = P::Renderer> + 'static,P::Theme: DefaultStyle,
{}

这个东西就比较复杂,这里主要核心是runtime模块通过userinterface管理关键的绘制工具,以及winit的库的消息进行绘制。
这个东西比较复杂,不再详细介绍。

后记

这里最关键的完整绘制winit申请绘制,以及绘制模块比如skia额绘制过程没有详细介绍,这里暂时不详细介绍了,等待以后有空再补充把。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/477538.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++注释

目录 1. 什么是注释 2. 语法 2.1 单行注释 2.2 多行注释 2.3 示例 3. 注释源代码的方法 3.1 使用多行注释 3.2 使用预处理器指令 #if #endif 3.3 使用条件判断语句 if (false) 4. 不能用宏定义&#xff0c;组成注释 5 // \ 会将源代码中的下一行也被当作注释中的一部…

使用itextpdf进行pdf模版填充中文文本时部分字不显示问题

在网上找了很多种办法 都解决不了; 最后发现是文本域字体设置出了问题; 在这不展示其他的代码 只展示重要代码; 1 引入扩展包 <dependency><groupId>com.itextpdf</groupId><artifactId>itext-asian</artifactId><version>5.2.0</v…

web——sqliabs靶场——第十三关——报错注入+布尔盲注

发现是单引号加括号闭合的 尝试联合注入 发现不太行&#xff0c;那尝试报错注入。 测试报错注入 unameadmin) and updatexml(1,0x7e,3) -- &passwdadmin&submitSubmit 爆数据库 unameadmin) and updatexml(1,concat(0x7e,database(),0x7e),3) -- &passwdadmin&a…

QT如何共享文件+拷贝文件

QString sharedFolderPathImg "\\\\" IP "/profileImage/"; // 更换为你的共享文件夹路径QDir dirImg(sharedFolderPathImg);dirImg.setFilter(QDir::NoDotAndDotDot | QDir::AllEntries);QVector<QString> curFileEntryArrayImg dirImg.entryList…

跟《经济学人》学英文:2024年11月23日这期 Why British MPs should vote for assisted dying

Why British MPs should vote for assisted dying A long-awaited liberal reform is in jeopardy in jeopardy&#xff1a;在危险中 jeopardy&#xff1a;美 [ˈdʒepərdi] 危险&#xff1b;危机&#xff1b;风险&#xff1b; 原文&#xff1a; THIS NEWSPAPER believes …

【es6进阶】vue3中的数据劫持的最新实现方案的proxy的详解

vuejs中实现数据的劫持,v2中使用的是Object.defineProperty()来实现的&#xff0c;在大版本v3中彻底重写了这部分&#xff0c;使用了proxy这个数据代理的方式&#xff0c;来修复了v2中对数组和对象的劫持的遗留问题。 proxy是什么 Proxy 用于修改某些操作的默认行为&#xff0…

D73【 python 接口自动化学习】- python 基础之正则表达式

day73 正则表达式-元字符匹配 学习日期&#xff1a;20241119 学习目标&#xff1a;正则表达式--133 正则表达式-元字符匹配 学习笔记&#xff1a; 元字符匹配 数量匹配 实践操作 总结 字符串的r标记表示&#xff0c;字符串内转移字符无效&#xff0c;作为普通字符使用正则…

Python浪漫之画明亮的月亮

目录 1、效果展示 2、完整版代码 1、效果展示 2、完整版代码 import turtledef draw_moon():# 设置画布turtle.bgcolor("black") # 背景颜色为黑色turtle.speed(10) # 设置绘制速度# 绘制月亮的外圈turtle.penup()turtle.goto(0, -100) # 移动到起始…

微信小程序包之加农炮游戏

微信小程序 - 气球射击游戏 项目简介 这是一个简单有趣的微信小程序射击游戏。玩家通过控制屏幕底部的加农炮&#xff0c;射击从上方降落的蓝色气球。游戏考验玩家的反应能力和瞄准技巧。 游戏规则 点击屏幕任意位置发射炮弹大炮会自动对准点击位置击中气球获得10分如果气球触…

autogen+ollama+litellm实现本地部署多代理智能体

autogen 是一个专门为大语言模型 (LLMs) 驱动的自治代理 (autonomous agents) 设计的 Python 库,由 Microsoft 开发和维护。它通过高度模块化和可扩展的架构,支持用户快速构建和运行多代理系统,这些代理可以在没有明确人类干预的情况下协作完成复杂任务。AutoGen 支持以最少…

分公司如何纳税

分公司不进行纳税由总公司汇总纳税“子公司具有法人资格&#xff0c;依法独立承担民事责任;分公司不具有法人资格&#xff0c;其民事责任由公司承担。”企业设立分支机构&#xff0c;使其不具有法人资格&#xff0c;且不实行独立核算&#xff0c;则可由总公司汇总缴纳企业所得税…

HashMap源码详解

提醒你一下&#xff0c;用电脑或者平板看&#xff0c;手机屏幕小&#xff0c;图片会看不清楚&#xff0c;源码不方便看 基础前置 看该篇文章之前先看看这篇基础文章 HashMap底层原理-CSDN博客 先来看HashMap里面的一些参数。 1.DEFAULT_INITIAL_CAPACITY 默认的数组初…

09 —— Webpack搭建开发环境

搭建开发环境 —— 使用webpack-dev-server 启动Web服务&#xff0c;自动检测代码变化&#xff0c;有变化后会自动重新打包&#xff0c;热更新到网页&#xff08;代码变化后&#xff0c;直接替换变化的代码&#xff0c;自动更新网页&#xff0c;不用手动刷新网页&#xff09; …

动态反馈控制器(DFC)和 服务率控制器(SRC);服务率和到达率简单理解

目录 服务率和到达率简单理解 服务率 到达率 排队论中的应用 论文解析:队列等待成本动态感知控制模型 动态反馈和队列等待成本意识: 服务速率调整算法: 动态反馈控制器(DFC)和 服务率控制器(SRC) SRC公式4的原理 算力资源分配系统中的调整消耗 举例说明 服务…

九、FOC原理详解

1、FOC简介 FOC&#xff08;field-oriented control&#xff09;为磁场定向控制&#xff0c;又称为矢量控制&#xff08;vectorcontrol&#xff09;&#xff0c;是目前无刷直流电机&#xff08;BLDC&#xff09;和永磁同步电机&#xff08;PMSM&#xff09;高效控制的最佳选择…

web-03

CSS回顾 选择器 标签选择器 标签{}ID选择器 标签中定义ID属性。 #ID值{}类选择器 标签中使用class属性 .类名{}关于DIV/span div任意的大小的长方形&#xff0c;大小css&#xff1a; width, height控制。—换行 span-- 一行内 CSS常用属性 width/height 宽度/高度 定义&…

Excel求和如何过滤错误值

一、问题的提出 平时&#xff0c;我们在使用Excel时&#xff0c;最常用的功能就是求和了&#xff0c;一说到求和你可能想到用sum函数&#xff0c;但是如果sum的求和区域有#value #Div等错误值怎么办&#xff1f;如下图&#xff0c;记算C列中工资的总和。 直接用肯定会报错&…

蓝桥杯每日真题 - 第22天

题目&#xff1a;&#xff08;卡片&#xff09; 题目描述&#xff08;12届 C&C B组B题&#xff09; 解题思路&#xff1a; 该问题要求用数字卡片从 1 开始拼出整数&#xff0c;直到某一时刻不能拼出时停止。要确定拼到哪个最大整数&#xff0c;需要统计 每个数字“1”被用…

Ubuntu20.04 Rk3588 交叉编译ffmpeg7.0

firefly 公司出的rk3588的设备&#xff0c;其中已经安装了gcc 交叉编译工具&#xff0c;系统版本是Ubuntu20.04。 使用Ubuntu20.04 交叉编译ffmpeg_ubuntu下配置ffmpeg交叉编译器为arm-linux-gnueabihf-gcc-CSDN博客文章浏览阅读541次。ubuntu20.04 交叉编译ffmpeg_ubuntu下配…

python代码制作数据集的测试和数据质量检测思路

前言 本文指的数据集为通用数据集&#xff0c;并不单是给机器学习领域使用。包含科研和工业领域需要自己制作数据集的。 首先&#xff0c;在制作大型数据集时&#xff0c;代码错误和数据问题可能会非常复杂。 前期逻辑总是简单的&#xff0c;库库一顿写&#xff0c;等排查的时…