IMGUI的设计

(按:
web领域,GUI编程有着学习曲线,
琐碎,平凡的GUI,为什么需要复杂的学习呢,
我希望可以设计实现一个简单,能立即上手的GUI框架)

我曾经在预研工作中实现过一版IMGUI,作为用户扩展的GUI,
模拟器 API文档

IMGUI明显的好处是,用户不需要熟悉GUI的概念,比如html / xml / css或其它的 GUI 领域特定语言(DSL),只需要有一般脚本编程技能就可以了,

const button = Button.create({text: 'click me'});
if (button.click) {
    console.log(’i am clicked, dont stop‘)
}

一目了然,是吧

IMGUI的问题

问题来了,那为什么大多数GUI框架,未选择IMGUI?

首先,这问题不成立,在早期C编程时代,大家写的GUI都是IMGUI,因为那时,大家写UI本来就是即时绘制

// 每祯循环,所以叫做“即时”模式
void onUpdate() {
  // 绘制 button
  drawButton("click me", "thisButtonID");
  Event clickEvent;
  // 过滤是否有点击消息
  filterEvent(&clickEvent);
  if(clickEvent.valid) {
    // 是否点击在button区域中 
    if( getRec("thisButtonID").include(clickEvent.point) ) {
        printf("i am clicked")
    }
  }
}

然后,这问题很有价值,

GUI思想一直在发展,从早期绘制,到GUI编程成为一个相对独立的编程,出现了面向对象编程,将
GUI关注于 对象事件,Button作为控件对象

const btClickMe = new Button(...)  
btClickMe.on('click', ()=>{
   console.log('i am clicked')
})

更近一步地,因为GUI相当关心排版问题,即需要组织一颗GUI树,出现了GUI排版描述语言, 即一般的XML/HTML/JSON表示

const GUIDesc = {
    type: "layout", // 是排版容器节点
    direction: "vertical", // 竖直排版
    align : {
        longtitude: "begin", // 径度对齐,begin即向上对齐
        latitude: "begin", // 纬度对齐,begin即向左对齐
    },
     
    children : [
        {
            type: 'button', // 一个button组件
            text: 'click me',
            onClick: ()=>{ // 点击回调
                console.log('i am clicked');
            },
            children: [...]
        }
    ]
}
 
const gui = createGUI(GUIDesc);

这看起来有点像HTML了,是不?
这种方式对于良好组织复杂GUI有好处,但对于制作,比如说一个面板,简单的工具界面,反而显得复杂。

另外,
IMGUI一般被人认为影响效率, 它的实现经常每次事件都会有两次执行,一遍逻辑,再一遍layout输出,但也许有巧妙的设计可以避免效率上的开销;
IMGUI 混合逻辑和渲染,—— 有时是缺点,它影响大型GUI的整体设计,但在用户扩展里,经常是优点,而且,运用组件化实践,它可以做到通用化的GUI编程;

IMGUI的需求

需求来自两方面,

GUI编程的学习成本开始变高(你需要学习一个描述型语言,学习框架用法才能产生一个简单GUI),不能满足立即上手的需求(很多人不需要成为专家),这块先不表,

另一方面,现代的GUI越来越有动态性,传统上,XML/HTML擅长表达静态页面,如果兼容做动态页面,则需要和代码结合的一种表示方法,比如说 react 的 jsx文件,vue的 嵌入表达式。

如果有这样一个需求,如果上有一个按钮(打开/收起),下面是一张图,随着按钮点击,图会展开和收起,
IMGUI是这样做的

const button = Button.create({text: 'click me'});
if(button.click){
    isPicShown = !isPicShown; // toggle state       
}
let pic = null;
if(isPicShown) {
      pic = Image.create('xxx.jpg')
}

可以看到, 做高交互性的GUI,是 排版和逻辑的混合,很适合直接用代码表达,这里IMGUI是相当有价值的

IMGUI的改进

之前说过,GUI的思想在发展,而我想的是,大大加强IMGUI,让之能适应大多数情况的GUI设计,

关键的几个设计点,

  1. 每个GUI对象有构造,内部状态,外部属性
  2. 在渲染更新函数里,渲染,事件,生命周期都写在一起
GUIDefine("Button") {
  arguments:{ // 这里构造属性,比如 Button.create(...arguments) 这里的表示
      ...
  },
  states: { // 这里是内部持久状态
      ...
  },
  export: { // 这里是外部属性, 比如说 button.isClick == true
      ...
  }
  onUpdate: (event) => {
      if(event.afterAttach) { // 生命周期事件
           
      } else if (event.beforeDetach) {

      }
      rect = Rect.create(...)
      clickEvt = event.system.click
      if(rect.include(clickEvt.position) ) { // 比如这里处理系统点击事件,但真实并不会这样做,请思考为什么?
          triggerEvent(this.isClick, true) // 这样就可以if(button.isClick) 了
      }
  }
}
  1. 用样式模板+属性覆盖,做为theme方案
// theme 是UI模板
GUI.setTheme('defaultTheme');
// ...
Button.create({Theme="xxx", text:"click me", color:0x000}); // color 已经包含在

  1. 如何定义UI模板(theme)
// 对所有组件,给予一个默认的属性
UITemplate("defaultTheme") {
Button = {
 color:0xbbb;
 ...
};
panel = {
 background:...,
}
}


// 第二种方式可以在组件里,加入几种theme的本组件属性
GUIDefine("Button") {
// ...
ThemeOf("defaultTheme", {
  color:"xxx";
},
ThemeOf("deepinTheme", {
  color:"xxx";
}
}

// 第一种适合实现在UI库,整体的theme定义
// 第二种特别适合用在 用户自定义组件里

你可能感兴趣的:(IMGUI的设计)