XUI是xboot内置的立即模式GUI框架,代码量很低,但功能绝对强大,所想即所得,无需各种设计器辅助,随心所欲敲你的代码,就行了,当你敲完了,UI开发工作就结束了。
离线
这个项目demo采用xboot的主线代码,感兴趣的可以研究,不要钱的,学到了,就是自己的了
https://github.com/xboot/xboot
离线
补充一下原始设计稿,不是贴图喔,上面的界面是纯代码实现。
离线
离线
发现是不是有点不一样,设计稿对于xui最大的意义,就是提供颜色尺寸等信息
离线
如果感兴趣的人多的话,我可以再讲讲XUI的细节,先说个概括性的。
GUI实现分为三种设计模式:
1,保留模式,传统GUI,都是使用此方案,优点是成熟,缺点是开发繁琐
2,立即模式,诞生于游戏开发领域,解决游戏交互,xui就采用此模式,此模式优点,是代码量低,所想所得,灵活性强1,编写事情响应得心应手,缺点是纯代码,驾驭不了代码的新手,会退避三分
3,声明式,这个在web领域,如火如荼,什么VUE啥的,很现代,优点是,效果赞,赏心锐目,而且现代,缺点是,需要脚本语言配合,而且需要描述能力稍强点的语言,再有就是性能需求也是较高。嵌入式裸奔系统或者各种rtos没有见到过有这种的。不知以后会不会有这玩意儿。
离线
XUI里面没有任何回调函数,所有的事件响应都是 在if else 里面,是不是觉得不可以思议。来事件了,条件判断会自动转向
离线
https://gitee.com/xboot/xboot/blob/master/src/kernel/command/cmd-overview.c
这个链接,是一个测试demo,大部分控件,都展示了。
if(xui_begin_tree(ctx, "Normal Button"))
{
xui_layout_row(ctx, 3, (int[]){ 100, 100, -1 }, 40);
for(int i = 0; i < 8; i++)
{
xui_button_ex(ctx, 0, wcstr[i], (i << 8) | XUI_OPT_TEXT_CENTER);
}
xui_end_tree(ctx);
}
比如这段代码,就是如果点击了树形控件里面的Normal Button标题,则会自动创建8个按钮,每行3个,前2个100像素宽,40高,最后一个完全填充完剩余空间。
感觉是不是很神奇,这一点代码就可以做这么多事了。
离线
是的,乐鑫的wifi模块
离线
icon是矢量字体实现的,渲染过程利用了freetype,就是流行的icon font
离线
贴一个overview demo的演示视频,可以看看究竟实现了哪些控件,要扩展控件也是比较简单的,一般一个控件也就不到200行代码,也可自定义高级别的的控件。
离线
高级别的组件可以由各种低级别的组件组合而成,也就是说,你实现了一个模拟时钟界面,那么这个界面可以变成一个控件,导出一个控件函数,就可以随便调用了,需要注意的事,可变的控制信息需要通过参数传递进去,不能共享变量,不然创建多个控件时会导致同步变化。
离线
立即模式是个大循环,里面不断调用各种控件,每一帧只要屏幕上看得见的都会调用一遍,在调用时会生成绘图指令队列,再全部调用完后,重排绘图指令,做脏矩形优化,找到需要更新的区域,执行相关的绘图指令。事件处理过程就是在控件的调用里,事件会改变绘图指令,当绘图指令变化了,界面就跟这变化了
离线
因为是个大循环,感觉效率会很低,但这里做了很多优化,比如,比如当屏幕不变化时,也要去重刷,明显是浪费cpu资源,在这里用hash算法优化了,每一帧的所有绘图指令,都进行hash运算,当hash值变化了,就代表屏幕内容变化了,这样才需要执行具体的绘图指令。但光有这一个部分优化,还是不够的,如果屏幕仅仅有一小块区域变化了,hash值变了就进行全屏刷新绘制,效率也是很低的,所有,这里又将屏幕分成无数个格子,每个格子都有一个hash值,如果这个格子的hash值变化了,就代表这个格子需要重新绘制,这样就实现按需刷新,也就是脏矩形技术。
离线
经过优化后,这个大循环基本能做到千帧以上,逻辑上的响应速度是很快的,瓶颈主要是在渲染部分,这个看硬件渲染看硬件绘图能力,千帧UI的表现就是很灵敏,没有迟钝的现象,可以看上面的演示视频,当然你绘图拖后腿了,那就没辙了,谁叫你画得慢呢,立即UI,都是通过指令来生成各种图形的,贴图在这里是二等公民,因为都是指令,所以整个UI都是矢量的,每个控件都支持无限缩放,自动布局。看视频最后的演示,里面调整了主题参数,然后一个个按钮都变得特别大
离线
优化了下xui,避免多次重复计算字体尺寸,在F1C100S平台上测试帧率提升了4帧
字体测量,布局,这个还是比较耗资源的。
离线
这个就是一个美工用的工具,类似ps而已
离线
可以的,可以看那个cmd-overview.c,看里面怎么写的,千万别用保留模式的思维来思考,换一种思维,你会发现,一切都是顺其自然的,纯线性思维。
离线
下面是一个简单的demo代码,一个窗体里创建了3个控件,一个label,独占一行,显示value变量的值,两个button,每个100像素宽,在同一行,其中一个点击后执行加一操作,另一个执行减一操作。看着着代码应该更容易理解怎么编写UI吧。
static void test_window(struct xui_context_t * ctx)
{
if(xui_begin_window(ctx, "Test Window", NULL))
{
static int value = 0;
xui_layout_row(ctx, 1, (int[]){ -1 }, 0);
xui_label(ctx, xui_format(ctx, "value = %d", value));
xui_layout_row(ctx, 2, (int[]){ 100, 100 }, 0);
if(xui_button(ctx, "add"))
value++;
if(xui_button(ctx, "sub"))
value--;
xui_end_window(ctx);
}
}
下面是演示视频
最近编辑记录 xboot (2021-04-26 17:49:23)
离线
第一个问题,绝大部分控件的状态是由用户来维护,但某些控件,为了方便使用,xui自动负责维护了,这个需要在实现控件时,跟一般控件有些区别,也就是说,系统可以自动维护状态,比如树形控件的展开和折叠,窗口的显示与关闭,toogle开关的缓动动画过程,这些是控件自动维护的,但checkbox,radiobutton等状态是需要外面的变量来维护的。
第二个问题,通过描述文件生成ui,界面的描述,是没有任何问题的,但你事件响应就无法编程了,没有任何响应的界面,是没有任何意义的。
通过api改变控件的状态,这种是保留模式特有的,这立即模式根本就不需要api来改变控件状态,你直接改变变量的值,ui就跟着变化了。
控件和变量是天然绑定的,控件可以改变变量,同样变量也可以改变控件。
最近编辑记录 xboot (2021-04-26 20:07:23)
离线
关于控件和变量天然绑定,这个可以看number控件,这个控件可以绑定一个变量,变量类型有多种,char,int,float,double等都是可以绑定的
static inline int xui_number(struct xui_context_t * ctx, double * value, double low, double high, double step)
static inline int xui_number_float(struct xui_context_t * ctx, float * value, float low, float high, float step)
static inline int xui_number_int(struct xui_context_t * ctx, int * value, int low, int high, int step)
static inline int xui_number_uint(struct xui_context_t * ctx, unsigned int * value, unsigned int low, unsigned int high, unsigned int step)
static inline int xui_number_char(struct xui_context_t * ctx, char * value, char low, char high, char step)
static inline int xui_number_uchar(struct xui_context_t * ctx, unsigned char * value, unsigned char low, unsigned char high, unsigned char
number控件绑定某个变量后,就会双向控制,滑动鼠标可以改变变量的值,同样,改变变量的值,可以修改控件的显示状态,滑动的范围,步长等参数,都可以在调用控件时传递参数来设置。
if(xui_begin_tree(ctx, "Normal Number"))
{
static double n[8] = { 10, 20, 30, 40, 50, 60, 70, 80 };
xui_layout_row(ctx, 1, (int[]){ -1 }, 0);
for(int i = 0; i < 8; i++)
{
xui_number_ex(ctx, &n[i], -1000, 1000, 1, "%.2f", (i << 8) | XUI_OPT_TEXT_LEFT);
}
xui_end_tree(ctx);
}
这个代码是创建8个double型number控件,从-1000 到1000,步长为1。这样这个double变量,就限制了变化范围,以及变化的步长,可以通过ui来双向控制。
离线
XUI记录的是绘图指令,每条指令都有影响的空间区域且记录在指令里,将所有指令重排,计算出影响了到了哪些区域再做脏矩形优化。一个控件由多条绘制指令组合而成,其实所谓的控件仅仅是一个函数,并没有特别的,复合控件,就是多调用了很多其他函数而已。控件的概念本身是弱化了,只不过方便人使用而抽象出来的。从这一点上看,任何奇形怪状的控件都是可以做出来的,不一定是矩形区域,只要能画得出来的都可以。立即UI特别灵活,少量的代码就可以作出复杂的界面,在嵌入式领域还是有优势的,谁都讨厌动不动就上万行的代码。现在xui,23个控件总共才3000多行,其中框架就占了近2000行,也就说说一个控件才需100行代码,我想这是立即UI最大的优势吧。当然耦合性强,这也是他的缺点。
声明式UI肯定是未来的趋势,但这个需要强大的脚本语言配合,纯编译型语言是不可能实现声明式UI的。Jetpack Compose理念最大的变化就是将原来的继承关系变成了顶级函数间的组合关系,摒弃类,而拥抱函数。
再补充一句,声明式UI可以说是进化版的立即UI,就是不用用户维护变量版本的立即UI。为何能做到不要用户维护,那是因为引入了脚本语言,可以动态记录各种状态。
离线
是的,lua理论上是可以实现声明式GUI的,但现在还没见到相关工程,有lua版的react移植,但react仅仅是框架,排版,渲染,等都是自己实现,工作量还是很大的,这个领域我会一直关注,如果思路成熟了,真可能会去实现个lua版的声明式gui,现在xboot里,支持各种c版app,lua版app,可同时运行,不管选用哪种gui框架,都不冲突。之所以开发个c版本的gui,就是考虑到嵌入式领域,某些简单的案子,完全用c就可以胜任了,没必要上那么高阶的技能,况且lua也不是每个人都懂,多少是有点学习成本的。
编译型语言支持声明式ui,这一点我现在还没想明白,如果要让c支持声明式ui,理论上也有可能吗?第一感觉是不能。
离线
新版本,支持wifi,485更新界面信息
离线
从另外一个角度看,虚拟dom是否可以看成绘制命令呢,diff算法,就相当于比较两帧之间的绘制命令差异。可能一个是宏观的,一个是微观的吧
离线
是的,就是状态不好封装,还有具体的事件响应没办法描述,语言能力在声明式UI显得很关键,基本有什么样语言就有什么样的表达,还是看语言的表达能力。
离线
给xui添加滑动支持,采用阻尼系统模型
离线
中文界面启动速度优化,其中logo显示后会等待1秒,再执行APP
离线
软键盘,输入法?
离线
写个键盘控件没多少行代码,关键要好看的话,这个得找到漂亮的皮,才行
离线
启动速度及通信测试,持续通信45分钟,没有发现异常。
离线
区分拖动与滑动事件,滑动时可以急停,提升用户体验
离线
支持通过网络和485传输图片,因为传输协议层中间是通用的json数据,所以图片都是经过base64编码后,json格式打包传送到板子上,板子收到完整帧后,先base64解码,然后再jpg或png格式解码,解码后的内容直接生成surface_t对象,可通过xui_image 控件显示对象。
离线
基于前端技术的本地APP开发
离线
看你内存多大了,靠内部K级的SRAM肯定是不可以的
离线
给屏幕打上水印,任何界面都会有,有人能猜到究竟是想干啥吗?
离线
windows可以开发的,不过,体验差很多,开发效率也低很多。
离线
不需要,有自动布局系统,布局相关函数的调用一下,就可以按你心里所想得布局规则,布局了。
离线
要是还要自己去算坐标,不是累死个人,而且,定死坐标的那种,怎么可能做到自适应,且矢量化的UI系统。任意缩放,靠指定坐标来实现,是不敢想象的。XUI里面任何一个控件都无需传递任何坐标参数信息,也不会存储各种坐标,全靠自动布局系统来管理布局。
离线
立即UI想做任何效果都可以的,通过按键来操作UI,也是可以的,仅需要声明一个变量用来存储当前激活的控件
if(xui_tabbar(ctx, 0xf26c, "Turn off backlight", item == MENU_ITEM_LCD))
item = MENU_ITEM_LCD;
if(xui_tabbar(ctx, 0xf4ad, "Device infomation", item == MENU_ITEM_INFO))
item = MENU_ITEM_INFO;
if(xui_tabbar(ctx, 0xf011, "Shutdown", item == MENU_ITEM_SHUTDOWN))
item = MENU_ITEM_SHUTDOWN;
比如这段代码item是一个枚举型,代表当前激活的控件,你按按键后去改变item的值就可以了,当然如果你通过触摸控件,item的值也可以跟着变化,算数据双向绑定。
离线
现在的XUI比较依赖XBoot,是否能够剥离出来,作为一个单独的GUI框架呢
离线
@海石生风
XML打包成二进制格式,本质还是一个配置文件,那如何用xml或者二进制描述事件呢,以及在xml里如何动态修改控件状态或者外部变量呢,如果做不到这一点,就不能称之为声明式,如果仅仅是普通的xml描述界面,连响应式都算不上。
离线
可以的,什么效果都可以做到,对于在一个控件之上,再绘制一个控件,需要主动调用布局相关函数,不然,会依次向下排列的。
离线
可以调用这个布局函数
void xui_layout_set_next(struct xui_context_t * ctx, struct region_t * r, int relative);
离线
你这写法不正确,xui_begin和xui_end之间,是会疯狂调用的,你这写法等于无限分配一个图片,当然后面的肯定无法分配成功,没有内存了。合理的写法,是外部分配好,再使用,或者引入图片cache机制。才可以这样写。
离线
ReactJS这种是未来的趋势,大家都在追求效率以及体验,多种语言互相切换,还是很烦人的,lua可以说是最牛逼的胶水语言,但尽管如此,跟C交互,还是有很多细节需要照顾到,要开发声明式UI,必须从语言选择开始,否则再怎么设计,都是隔靴搔痒。
离线
WASM技术 + reactJS,应该算终极方案,嵌入式上实现一个wasm虚拟机,然后什么都可以搞定了
离线
xui_layout_begin_column是纯逻辑实现,用于实现包裹多列布局的代码,xui_begin_panel当然也能搞出这种多列布局,但自身就是一个具体控件。前者是一个布局函数,后者是控件。
离线