介绍
本文将向您展示GuiLite如何工作,架构是什么样的,然后您将了解为什么GuiLite是最小和最简单的GUI库。
快速回答:
- 只需5,000行C ++代码
- 只使用基本的C ++特性(类,虚函数),没有复杂的语法
- 所有渲染都基于draw_pixel,没有算法
- 完整文档和小型现场演示供参考
背景
- GuiLite是一个具有5000行C ++代码的准系统GUI框架,它可以在所有平台上运行(例如,iOS,Android,Linux,Windows,macOS,Docker和带或不带OS的MCU)。
- GuiLite是可嵌入的,它在其他UI框架内运行(例如,Qt,MFC,Winform,Cocoa)。您可以同时使用GuiLite功能和主机UI功能。
- GuiLite独立于OS和图形库呈现UI
- GuiLite可以使用多种语言(例如,Swift,Java,C#,Golang)。
以下是一些演示:
GuiLite做什么?
GuiLite只做两件事:小部件管理和渲染。
小部件管理包括:
- 添加/删除GUI元素(例如:按钮,标签,对话框),设置标题和位置
- 调度/响应输入消息:通过分析,来自消息的位置信息,GuiLite将找到应该响应的小部件,以及呼叫响应功能(例如
on_mouse_click
,on_keyboard_click
) - 调度/响应客户消息:通过分析消息的ID,GuiLite将找到响应函数,并调用它(例如
on_timer
,on_custmer_refresh
)
渲染包括:
- 绘制像素,直线,矩形
- 设置框架图层:在绘图时,应输入框架图层索引以使图形位于特定图层
- 更新帧图层:当帧图层更改时(例如:打开/关闭对话框),GuiLite将确定应在屏幕上显示哪个帧图像素
注意:渲染不依赖于窗口小部件管理。在某些情况下,您可以在GUI系统中直接渲染行/文本/位图而不使用任何小部件。例如:有限的资源MCU平台。
如何自定义/扩展GuiLite?
为了使GuiLite简单明了,GuiLite只提供基本的小部件(例如:按钮,标签,键盘,spinx等)实现。如果你想在小部件上做更多的事情,你可以这样做:
- 直接在GuiLite中更新小部件代码
- 添加新的窗口小部件文件,并通过引用GuiLite窗口小部件代码实现新的窗口小部件。
如果你想扩展绘图方法(例如,绘制圆/日蚀等),你可以直接在surface.cpp中添加你的方法。
怎么渲染?见功能draw_xxx()
在surface.cpp,word.cpp,bitmap.cpp。
代码介绍
核心文件夹:
- 为所有平台实现消息调度/响应,多帧层管理和呈现
- Windows / Linux / Android / iOS / macOS上的适配器GuiLite或适配器文件夹中的无操作系统
小部件文件夹:
- 这是控制器(例如,按钮,标签,键盘)和容器(例如,视图,对话框,翻页)的代码; 您可以通过修改此代码来重绘自己的小部件。
- 这里是消息转换的代码(例如,手指按下/释放),它将手指按下/释放消息传递给已被触摸的小部件,并且如果定义则调用回调函数。
小部件管理
窗口小部件包括button
,spinbox
,label
,keyboard
,dialog
,view
; GuiLite将按功能链接所有小部件:connect,然后GuiLite将很容易地快速查找/添加/删除任何小部件。所有的连接看起来像一棵树。
例如:当您单击按钮时,设备获取您的手指位置(x,y)将发送树的根,GuiLite将通过比较位置信息找到您单击的按钮,然后调用响应函数(例如,重绘按钮/更改按钮状态)。
如何创建小部件?
所有小部件都源自class c_wnd
。当实例化类时,将创建窗口小部件,此时,窗口小部件仍然单独,与UI系统无关,无法响应任何用户操作。
如何链接/取消链接小部件
通过使用“ connect()
”函数,我们可以将此窗口小部件链接到窗口小部件树,然后窗口小部件可以响应用户触摸/键盘操作。
int c_wnd::connect(c_wnd *parent, unsigned short resource_id, const char* str,
short x, short y, short width, short height, WND_TREE* p_child_tree )
{
if(0 == resource_id)
{
ASSERT(FALSE);
return -1;
}
m_resource_id = resource_id;
m_parent = parent;
m_status = STATUS_NORMAL;
if (parent)
{
m_z_order = parent->m_z_order;
m_surface = parent->m_surface;
}
if(NULL == m_surface)
{
ASSERT(FALSE);
return -2;
}
/* (cs.x = x * 1024 / 768) for 1027*768=>800*600 quickly*/
m_wnd_rect.m_left = x;
m_wnd_rect.m_top = y;
m_wnd_rect.m_right = (x + width - 1);
m_wnd_rect.m_bottom = (y + height - 1);
c_rect rect;
get_screen_rect(rect);
ASSERT(m_surface->is_valid(rect));
pre_create_wnd();
set_str(str);
if ( 0 != parent )
{
parent->add_child_2_tail(this);
}
if (load_child_wnd(p_child_tree) >= 0)
{
load_cmd_msg();
on_init_children();
}
return 0;
}
通过使用“ disconnect()
”功能,从UI系统中取消链接此小组件。然后小部件仍然存在于内存中,但不再响应用户触摸/键盘操作。
void c_wnd::disconnect()
{
if (0 == m_resource_id)
{
return;
}
if (NULL != m_top_child)
{
c_wnd *child = m_top_child;
c_wnd *next_child = NULL;
while (child)
{
next_child = child->m_next_sibling;
child->disconnect();
child = next_child;
}
}
if (0 != m_parent)
{
m_parent->unlink_child(this);
}
m_focus_child = 0;
m_resource_id = 0;
}
渲染
渲染包括渲染方法和图形管理。
- 所有渲染方法都基于
draw_pixel()
功能 - 到目前为止,GuiLite支持3个帧图层,可以处理3层重叠场景
void c_surface::draw_pixel(int x, int y, unsigned int rgb, unsigned int z_order)
{
if (x >= m_width || y >= m_height || x < 0 || y < 0)
{
return;
}
if (z_order > m_max_zorder)
{
ASSERT(FALSE);
return;
}
rgb = GL_ROUND_RGB_32(rgb);
if (z_order == m_max_zorder)
{
return draw_pixel_on_fb(x, y, rgb);
}
if (z_order > m_top_zorder)
{
m_top_zorder = (Z_ORDER_LEVEL)z_order;
}
if (0 == m_frame_layers[z_order].rect.PtInRect(x, y))
{
ASSERT(FALSE);
return;
}
((unsigned short*)(m_frame_layers[z_order].fb))[x + y * m_width] = GL_RGB_32_to_16(rgb);
if (z_order == m_top_zorder)
{
return draw_pixel_on_fb(x, y, rgb);
}
bool is_covered = false;
for (int tmp_z_order = Z_ORDER_LEVEL_MAX - 1; tmp_z_order > z_order; tmp_z_order--)
{
if (TRUE == m_frame_layers[tmp_z_order].rect.PtInRect(x, y))
{
is_covered = true;
break;
}
}
if (!is_covered)
{
draw_pixel_on_fb(x, y, rgb);
}
}
int c_surface::set_frame_layer(c_rect& rect, unsigned int z_order)
{
if (rect == m_frame_layers[z_order].rect)
{
return 0;
}
if (rect.m_left < 0 || rect.m_left >= m_width ||
rect.m_right < 0 || rect.m_right >= m_width ||
rect.m_top < 0 || rect.m_top >= m_height ||
rect.m_bottom < 0 || rect.m_bottom >=m_height)
{
ASSERT(FALSE);
return -1;
}
if (!(z_order > Z_ORDER_LEVEL_0 && z_order < Z_ORDER_LEVEL_MAX))
{
ASSERT(FALSE);
return -2;
}
if (z_order < m_top_zorder)
{
ASSERT(FALSE);
return -3;
}
m_top_zorder = (Z_ORDER_LEVEL)z_order;
c_rect old_rect = m_frame_layers[z_order].rect;
//Recover the lower layer
int src_zorder = (Z_ORDER_LEVEL)(z_order - 1);
int display_width = m_display->get_width();
int display_height = m_display->get_height();
for (int y = old_rect.m_top; y <= old_rect.m_bottom; y++)
{
for (int x = old_rect.m_left; x <= old_rect.m_right; x++)
{
if (!rect.PtInRect(x, y))
{
unsigned int rgb = ((unsigned short*)
(m_frame_layers[src_zorder].fb))[x + y * m_width];
draw_pixel_on_fb(x, y, GL_RGB_16_to_32(rgb));
}
}
}
m_frame_layers[z_order].rect = rect;
if (rect.IsEmpty())
{
m_top_zorder = (Z_ORDER_LEVEL)(z_order - 1);
}
return 0;
}
渲染方法
- 基本渲染功能:
surface_cpp.cpp::draw_xxx()
- 位图渲染功能:
bitmap.cpp::draw_bitmap_xxx()
- 为了获得GPU优势,您可以
draw_xxx()
使用GPU功能进行重构
图形层
- 显示层:该层用于物理显示设备,一个UI只有一个显示层
- 表面层:一个显示层有许多表面层,一个表面层表示一个翻页
- 框架层:一个表面层具有许多框架层,一个框架层表示Z方向上的一个层
我们来看看如何创建一个显示:
c_display::c_display(void* phy_fb, unsigned int display_width, unsigned int display_height,
unsigned int surface_width, unsigned int surface_height,
unsigned int color_bytes, unsigned int surface_cnt,
EXTERNAL_GFX_OP* gfx_op)
{
if (color_bytes != 2 && color_bytes != 4)
{
log_out("Support 16 bits, 32 bits color only!");
ASSERT(FALSE);
}
m_width = display_width;
m_height = display_height;
m_color_bytes = color_bytes;
m_phy_fb = phy_fb;
m_phy_read_index = m_phy_write_index = 0;
memset(m_surface_group, 0, sizeof(m_surface_group));
m_surface_cnt = surface_cnt;
ASSERT(m_surface_cnt <= SURFACE_CNT_MAX);
for (int i = 0; i < m_surface_cnt; i++)
{
m_surface_group[i] = phy_fb ? new c_surface
(this, surface_width, surface_height, color_bytes) :
new c_surface_no_fb(this, surface_width, surface_height, color_bytes, gfx_op);
}
}