第 3 章 - GUIX 功能概述

本章包含高性能 GUIX 用户界面产品的功能概述。

执行概述

GUIX 可实现事件驱动的编程模型。 这意味着,GUIX 框架主要通过接收推送到 GUIX 事件队列的事件来完成驱动。 这些事件的处理过程发生在 GUIX 线程的上下文中,该线程是指在 GUIX 系统初始化期间创建的 ThreadX 线程。

GUIX 应用程序通过调用 GUIX API 函数创建窗口和子级小组件来定义用户界面,并通过调用用于定义各窗口或小组件类型的颜色、样式、字体和各种其他属性的附加 API 函数来自定义这些小组件的外观。 如果你使用 GUIX Studio 来创建用户界面屏幕的外观,GUIX Studio 应用程序将能为你完成调用 GUIX API 函数来创建显示内容的大部分工作。

GUIX 应用程序通过处理从 GUIX 事件队列检索到的事件来与系统用户和外部业务逻辑建立交互。 这些事件通常由 GUIX 小组件生成,但也可由外部线程创建。 推送典型的 GUIX 按钮后,该按钮会将事件发送到按钮的父窗口。 应用程序将通过为按钮推送事件提供处理程序来处理该按钮推送操作。

系统通常会为输入驱动程序等创建其他 GUIX 线程。 典型的触摸屏输入驱动程序会作为主 GUIX 线程之外的独立线程执行。 触控输入驱动程序通过将事件发送到 GUIX 事件队列来向 GUIX 线程发送触控信息。

由于动画等许多用户界面操作都需要准确的计时信息,GUIX 还实现了简单易用的计时器接口。 该计时器接口基于 ThreadX 计时器服务构建,会在系统启动时自动完成配置。

大多数 GUIX 软件都独立于任何硬件依赖项。 该框架需要使用特定于硬件的输入和图形驱动程序。 稍后我们将在第 5 章详细介绍如何实现这些特定于硬件的驱动程序。

初始化

必须先调用 gx_system_initialize 服务,再调用任何其他 GUIX 服务。 可以从 ThreadX _tx_application_define 例程(初始化上下文)或应用程序线程调用 GUIX 系统初始化。 gx_system_initialize 函数可创建 GUIX 事件队列,初始化 GUIX 计时器设备,创建主 GUIX 系统线程,并在执行应用程序期间初始化由 GUIX 维护的各种数据结构

gx_system_initialize 返回后,便可使用应用程序创建显示内容、画布、窗口、小组件,并自定义所有 GUIX 对象的属性。 许多 GUIX 对象创建 API 都可从 tx_application_define 或应用程序线程调用

应用程序接口调用

应用程序主要从 tx_application_define(初始化上下文)或应用程序线程发出调用。 请参阅第 4 章中针对每个 GUIX API 所述的“允许来源”部分,确定可从哪个上下文进行调用。

大多数情况下,对密集型活动的处理都会延迟到内部 GUIX 线程,包括所有事件处理和小组件/窗口绘制操作。

系统可随时从任何线程调用 GUIX API 函数。 但我们通常认为,该体系结构在分离时间临界业务逻辑与用户界面逻辑方面表现更佳。 根据显示大小和 CPU 性能,用户界面绘图操作有时可能需要很长时间,你通常不会希望延迟时间临界线程,以等待绘图操作完成。

内部 GUIX 线程

如前文所述,GUIX 有一个用于执行大部分 GUI 处理工作的内部线程。 此线程由应用程序软件创建,方法是依次调用 gx_system_initialize 和 gx_system_start

内部 GUIX 线程的优先级由 #define GX_SYSTEM_THREAD_PRIORITY 确定。 该值默认为 16(中等优先级),但你可在 gx_port.h 或 gx_user.h 头文件中指定该值来覆盖此默认值。

GUIX 线程时间片同样也由 #define GX_SYSTEM_THREAD_TIMESLICE 定义,其默认值为 10 毫秒。

系统线程的堆栈大小由位于 gx_port.h 头文件中的 #define GX_THREAD_STACK_SIZE 确定,但你也可在 gx_user.h 头文件中指定该值以覆盖原有值

内部 GUIX 线程执行循环由三项操作组成。 首先,GUIX 从 GUIX 事件队列中检索事件,并调度这些事件以便由 GUIX 窗口和小组件进行处理。 事件通常由定期计时器、输入设备(如触摸屏或小键盘)以及 GUIX 小组件本身(在处理用户输入时)推送到 GUIX 事件队列。 接下来,在处理完所有事件后,GUIX 会确定是否需要刷新屏幕。如果需要,则执行更新显示图形数据所需的处理操作,方法主要是通过调用已标记为“脏”的窗口和小组件的绘图函数。 最后,在收到一个或多个新输入事件之前,GUIX 会一直挂起 GUIX 线程。

事件处理

触控或笔输入事件的处理方式为:确定触控或笔输入像素位置下最顶层的窗口或小组件,然后调用该窗口/小组件的事件处理函数。 如果小组件理解笔输入事件,则其会根据该小组件的类型处理相关事件。 如果不理解,最顶层的小组件则会将触控或笔输入事件传递至小组件的父项进行处理。 此事件会在链中一直传递下去,直至得到处理或到达根窗口,届时系统将放弃此事件。

系统会将小键盘事件发送到含输入焦点的窗口/小组件。 输入焦点状态由 GUIX gx_system 组件进行维护。

计时器事件会一律调度到拥有计时器的窗口或小组件以供处理。

内部生成的事件(如按钮单击事件或滑块值更改事件)会一律发送到生成该事件的小组件的父项。 如果父项不处理该事件,则与触控或笔输入事件类似,系统会在链中将其一直传递下去。

绘图

完成所有事件处理后,GUIX 内部线程将确定是否需要更新任何显示,如果需要,则将调用相关的窗口/小组件绘图函数。 绘图完成后,GUIX 内部线程只需在其事件队列上等待下一个要进行处理的 GUIX 事件。

GUIX 实现了“脏区域”的概念,即每个小组件和画布中需要重新绘图的区域。 小组件的绘图区域只能到之前已标记为“脏”的区域。 调用小组件绘图函数时,所有绘图操作都将在内部被剪辑到之前定义的脏矩形中。 在此区域外的绘图尝试将遭到忽略。

小组件和窗口通过调用 API 函数 gx_system_dirty_mark 将自身标记为“脏”。 此函数会将整个小组件或窗口标记为需要重新绘图。 或者,还可以通过调用另一个函数 gx_system_dirty_partial_add 以只将窗口或小组件的一部分标记为“脏”。

这种将小组件标记为“脏”,然后仅在处理完所有输入事件时才重新绘制这些小组件的模型称为“延迟绘图”。 GUIX 延迟绘图算法和脏列表维护专为提高绘图效率而设计。 由于绘图操作通常非常昂贵,因此 GUIX 会尽量防止执行不必要的绘图。

到 GUIX“画布”时,绘图结束。 画布指系统保留以容纳图形数据的内存区域。 画布可能会也可能不会直接链接到硬件帧缓冲区,具体则取决于系统体系结构和内存限制。 必须先通过调用 gx_canvas_drawing_initiate API 函数打开用于绘图的画布,才可执行任何绘图。 此 API 会准备一张用来绘图的画布并构建好当前的“绘图上下文”。 GUIX 在刷新系统画布时,用于绘图的画布将会打开,绘图上下文亦会在调用小组件级别的绘图 API 之前构建完毕。 因此,小组件不需要在小组件绘图函数内的画布上启动绘图功能。

但是,如果应用程序想要在标准 GUIX 延迟绘图算法流之外对画布执行即时绘图操作,则该应用程序必须先直接调用 gx_canvas_drawing_initiate,然后再调用任何其他绘图 API 函数,并且必须在完成即时绘图后调用 gx_canvas_drawing_complete

用户输入

GUIX 支持具有预定义事件类型的触摸屏、鼠标和键盘设备。 你可以通过定义自定义事件类型或将自定义输入设备映射到预定义事件类型来利用其他输入设备。

与这些设备关联的操作将转换为由内部 GUIX 线程处理的事件。 必须准备好为支持触摸屏而编写的驱动程序级别软件并将这些软件发送至 GUIX 事件队列事件,以便用笔执行向下、向上滚动及拖动操作。 同样,小键盘输入驱动程序必须为按键和释放键输入生成事件。

模态对话框执行是指向用户呈现一个窗口,用户必须以某种方式关闭该窗口后,其他任何 GUIX 窗口或小组件才能接收用户输入。 模态对话框窗口显示时,该对话框将捕获所有用户输入,无论触摸或鼠标输入事件的 x、y 在何位置。

模态对话框由应用程序软件触发,方法是:先通过依次调用 gx_window_create 和 GUIX API 函数 gx_window_execute 来以正常方式创建窗口

调用 gx_window_execute 函数时,GUIX 将进入本地事件处理循环。 通过用户输入或调用 gx_window_close 关闭对话框窗口之前,gx_window_execute 函数不会返回给调用方。 因此,切勿从 GUIX 内部线程以外的任何线程调用 gx_window_execute 函数

定期处理

为提供显示效果、精灵动画和应用程序定期请求支持,GUIX 会使用一个 ThreadX 计时器。 此单一计时器可用于驱动所有与时间相关的 GUIX 需求。 默认情况下,通过在 gx_api.h 中定义的常数 GX_SYSTEM_TIMER_MS 设置的 GUIX 内部计时器处理的频率为 20 毫秒,除非该常数之前是在 gx_port.h 或 gx_user.h 的标头中进行定义。 应用程序可以通过以下方法更改默认频率:在构建 GUIX 库时使用编译选项或在 gx_user.h 中显式地重新定义该值

重要

请注意,GUIX 计时器频率以 RTOS 计时器标记表示,并由常数 GX_SYSTEM_TIMER_TICKS 定义。 系统会使用 GX_SYSTEM_TIMER_MS 和 TX_TIMER_TICKS_PER_SECOND 计算 GX_SYSTEM_TIMER_TICKS 的值。 用户可以通过重新定义 gx_port.h 或 gx_user.h 中的任何上述值来调整 GUIX 计时器频率和解决方法

显示驱动程序

显示驱动程序负责为核心 GUIX 代码提供一组绘图函数。 每个绘图函数的实现由驱动程序确定,如有可能,实现将会利用硬件加速支持。 绘图函数的工作方式通常是将像素数据写入内存缓冲区,其可能是物理帧缓冲区或者辅助缓冲区,具体取决于驱动程序体系结构。 许多驱动程序都使用两个帧缓冲区实现双缓冲,并通过调用缓冲区切换函数来完成对这些缓冲区的切换。 GUIX 会在适当的情况下从内部调用这些函数。 如果系统内存受限,则绘图函数只能写入单个内存帧缓冲区。

GUIX 为所有支持颜色深度和格式的每个低级别绘图函数都提供默认软件实现。 系统会通过在 GX_DISPLAY 结构中维护的函数指针调用这些函数。 创建特定于硬件的驱动程序时,这些驱动程序通常会用特定于目标硬件的函数覆盖一定数量的函数指针。

典型的硬件显示驱动程序可通过先创建所需颜色深度和格式的默认 GUIX 显示驱动程序来实现。 然后,硬件驱动程序将会替换那些需要针对特定硬件实现进行优化或自定义的函数。

GUIX 支持像素颜色格式,范围为 1-bpp 单色到 32-bpp a:r: g:b 格式。 GUIX 还支持每个广泛的颜色深度类别内出现的许多变化元素,例如 r:g: b 与 b:g: r 字节顺序、组合像素与字节对齐像素格式及阿尔法通道。 目前支持 25 种不同的颜色格式,但随着硬件供应商提供新的变化元素,此列表将不断增长。

显示内存体系结构

不同的硬件目标和显示会利用各种不同的显示内存体系结构,具体取决于目标的内存限制和应用程序的功能要求。 我们将在此概述其中一些常见的内存体系结构,并会要简要说明每个结构。

模型 1) 没有帧缓冲区,图形数据保留在外部 GRAM 中:

No frame buffer, graphics data held in external GRAM

在上述模型中,CPU 本地内存中不存在帧缓冲区的内存。 所有图形数据将存储在外部 GRAM 中,并且该外部 GRAM 将会与显示本身合并。 外部 GRAM 的接口可以并行或串行。 这种体系结构的成本非常低,但可能会在系统更新图形数据时出现不必要的撕裂效果。

模型 2) 一个本地帧缓冲区:

One local frame buffer

在此模型中,图形数据的内存将从将从可直接访问 CPU 的随机存取内存中分配。 必须存在专用硬件,才能从本地内存反复传输图形数据(以及计时信号)至显示。 此模型不同于模型 1,因为模型 1 的图形内存是可供 CPU 使用的本地 SRAM 或 DRAM 块。 这可能是存有堆栈和程序变量的相同内存。

模型 3) 本地帧缓冲区 + 外部 GRAM:

Local frame buffer + external GRAM

模型 3 是前两个模型的组合。 此模型中有充足的本地内存可容纳一个帧缓冲区。 此外,显示设备可提供外部 GRAM,并使用 GRAM 中提供的数据自动刷新设备。 此体系结构的优点在于提高了更新效率,因为我们通常可以使用板载 DMA 通道,在一个块传输中将修改后的部分本地帧缓冲区传输到外部 GRAM。 此模型还将消除可能会在前两个模型中出现的撕裂和闪烁现象,因为只有已完成的图形内容才会复制到外部 GRAM。

模型 4) Ping-pong 帧缓冲区:

Ping-pong frame buffers

模型 4 中有充足的内存可提供两个本地帧缓冲区。 在这种情况下,GUIX 会将一个帧缓冲区视为活动帧缓冲区,另一个视为工作帧缓冲区。 当显示更新或绘图操作正在进行时,它会在工作缓冲区中发生。 绘图操作完成后,缓冲区将切换,工作缓冲区变为活动缓冲区,而活动缓冲区变为工作缓冲区。 此模型还将消除可以在单个缓冲系统中观察到的屏幕闪烁和撕裂现象。

模型 5) 含画布合成功能的 Ping-pong 缓存区:

Ping-pong buffers with canvas compositing

在模型 5 中,可以创建任意数量的画布,直到达到可用内存上限。 画布可以按照应用程序中定义的方式叠加或混合在一起,以创建画布合成。 如果系统在刷新屏幕后创建新的复合缓冲区,活动与工作合成缓冲区会在与标准 ping-pong 缓冲区体系结构相同的操作中进行切换。 通过将画布混合到最终的输出合成中,模型 5 增加了执行屏幕淡出和混合操作的能力。

模型 6) 含外部 GRAM 的画布合成:

Canvas compositing with external GRAM

模型 6 基于模型 5 略作修改,只需要一个合成缓冲区,并且之后该合成缓冲区将传输到外部 GRAM。 此模型还支持全屏混合和覆盖。

字符串编码

默认情况下,GUIX 支持 UTF8 格式字符串编码。 你可以通过在 gx_user.h 头文件中定义 GX_DISABLE_UTF8_SUPPORT 来禁用对 UTF8 字符串编码的支持。 如果 UTF8 编码处于禁用状态,则 GUIX 内部将仅使用标准的 8 位 ASCII 加上拉丁语-1 代码页字符编码。 禁用 UTF8 字符串编码可略微减少 GUIX 库的占用空间,同时略微提高字符串处理和文本绘制函数的运行时执行速度。

UTF8 字符串编码具有以下特征:

  • ASCII 字符串占用的存储空间少于标准的 7 位 ASCII 编码。

  • 大多数 ANSI C 字符串函数使用 UTF8 字符串编码,且不用修改。

你可以使用 UTF8 字符串编码表示世界上的所有活动字符集,包括汉字字符集。

静态和动态字符串

分配给支持文本显示的 GUIX 小组件的字符串可以是静态定义的字符串常量,它们通常作为下述 GUIX 字符串表的一部分存放在常量存储器中;也可以是动态定义的字符串,即在运行时使用 sprintf 或 gx_utility_ltoa 等服务生成的字符串

动态字符串的示例可能包括在 GUIX 提示小组件中显示为数字的值,或者包含根据用户的位置和格式首选项动态设置格式的“时间/日期”字符串。 如果在运行时创建将分配给 GUIX 小组件(如 GX_PROMPT 或 GX_TEXT_BUTTON 小组件)的字符串,则你必须选择为运行时生成的这些字符串静态分配存储器(即全局字符数组),或者定义并安装动态内存分配器函数并使用 GX_STYLE_TEXT_COPY 样式,该样式将指示那些小组件创建已分配文本字符串的专用副本。

使用临时存储器(如自动字符数组)来保存动态生成的字符串,然后将该字符串分配给不含 GX_STYLE_TEXT_COPY 样式的小组件属于一种编程错误。 如果未启用此样式,则小组件只会复制所提供的字符串指针,并且必须静态分配字符串数据,否则小组件字符串指针最终可能会指向产生不可预知结果的垃圾数据。

传递 GX_STRING 参数

接受 GX_STRING 参数的 GUIX API 函数一律会验证 GX_STRING.gx_string_ptr 字段指向的字符串的长度与 GX_STRING.gx_string_length 字段的值是否相符。 如果这两个字段不一致,系统将返回 GX_INVALID_STRING_LENGTH 错误,并且调用返回的 API 不会接受字符串赋值。

出于安全考虑,GUIX 软件决不会在内部使用标准 C 字符串函数,例如 strlen 或 strcpy。 众所周知,如果动态获取字符串数据(通常指使用已连接应用程序的情况),这些函数会很容易受到恶意攻击。

5\.6 版之前的 GUIX 库版本已经定义了接受 (GX_CONST GX_CHAR *text) 作为参数的 API 函数。 尽管仍支持后向兼容性,但这些函数已经过时且已被接受 (GX_CONST GX_STRING *string) 作为输入参数的首选 API 函数替换。

系统默认启用不建议使用的文本处理 API,允许所有之前编写的应用程序使用 GUIX 库的最新更新干净地执行构建操作。 若要禁用不建议使用的文本处理 API,应将定义 GX_DISABLE_DEPRECATED_STRING_API 添加到 gx_user.h 头文件中。 所有新应用程序都应定义 GX_DISABLE_DEPRECATED_STRING_API,并且应仅使用替代 API 函数。 GUIX 库版 GUIX Studio 5.6 版或更高版本生成的所有输出文件将仅使用替代 API 函数。

下表列出了不建议使用和新定义的替换 API 函数名称:

不建议使用的函数名称 替换为
gx_binres_language_table_load gx_binres_language_table_load_ext
gx_canvas_rotated_text_draw gx_canvas_rotated_text_draw_ext
gx_canvas_text_draw gx_canvas_text_draw_ext
gx_context_string_get gx_context_string_get_ext
gx_display_language_table_get gx_display_language_table_get_ext
gx_display_language_table_set gx_display_language_table_set_ext
gx_display_string_get gx_display_string_get_ext
gx_display_string_table_get gx_display_string_table_get_ext
gx_multi_line_text_button_text_set gx_multi_line_text_button_text_set_ext
gx_multi_line_text_input_char_insert gx_multi_line_text_input_char_insert_ext
gx_multi_line_text_input_text_set gx_multi_line_text_input_text_set_ext
gx_multi_line_text_view_text_set gx_multi_line_text_view_text_set_ext
gx_prompt_text_get gx_prompt_text_get_ext
gx_prompt_text_set gx_prompt_text_set_ext
gx_single_line_text_input_text_set gx_single_line_text_input_text_set_ext
gx_system_string_width_get gx_system_string_width_get_ext
gx_system_version_string_get gx_system_version_string_get_ext
gx_text_button_text_get gx_text_button_text_get_ext
gx_text_button_text_set gx_text_button_text_set_ext
gx_text_scroll_wheel_callback_set gx_text_scroll_wheel_callback_set_ext
gx_utility_string_to_alphamap gx_utility_string_to_alphamap_ext
gx_widget_string_get gx_widget_string_get_ext
gx_widget_text_blend gx_widget_text_blend_ext
gx_widget_text_draw gx_widget_text_draw_ext

GUIX 字符串表

GUIX 字符串表和字符串资源已注册到 GUIX 显示实例中。

多显示系统中的每个显示都有其自己的字符串表,并且每个显示都可以使用自己选择的语言运行。由于特定于每个显示颜色格式和颜色深度,因此其他 GUIX 资源类型(颜色、字体和像素图)也会由 GUIX 显示组件进行维护。

尽管你可以手动创建应用程序字符串表,但大多数情况下,显示字符串表将由 GUIX Studio 应用程序定义为项目资源文件的一部分。 此外,可用语言也是在资源头文件中进行定义。 显示字符串表指应用程序字符串指针的多列表。 字符串表中的每一列表示应用程序支持的一种语言。 如果应用程序仅支持一种语言(如英语),则字符串表将只有一列。 你仍可随时添加对其他语言的支持,且无需修改应用程序软件。

活动字符串表通过调用 gx_display_string_table_set API 函数进行分配。 此函数由 GUIX Studio 生成的启动代码自动调用,但也可由应用程序直接调用以更改活动字符串表。

活动语言是通过调用 gx_display_active_language_set API 函数来分配的。 此函数可确定显示字符串表中处于活动状态的列。

调用此函数时,系统将向所有可视的 GUIX 小组件发送 GX_EVENT_LANGUAGE_CHANGE 事件,使其执行更新以显示新的活动字符串数据。

小组件和应用程序软件使用字符串 ID 值、gx_display_string_get_ext 或 gx_widget_string_get_ext API 函数解析静态定义的字符串。 这些函数会返回与给定字符串 ID 和当前活动语言存在关联的 GX_STRING。

双向文本显示

GUIX 为双向文本支持提供两种策略。

其一为,在 GUIX Studio 应用程序中执行双向文本重新排序。 使用此选项时,GUIX Studio 负责按其显示顺序向输出文件生成双向文本。 此解决方案不会对运行时性能造成影响,且无需向 GUIX 运行时库添加任何内容。 若要允许 GUIX Studio 生成 displayorder 双向文本字符串,你应在 GUIX Studio 语言配置对话框中选择“按显示顺序生成双向文本”复选框:

Configure languages

选择这些选项后,生成的资源文件将包含按显示顺序生成的双向字符串,并且无需在 GUIX 运行时库中执行其他处理。

其二为,在运行时执行双向文本重新排序。 如果应用程序必须要处理动态定义且并非由 GUIX Studio 应用程序生成的双向文本字符串,则支持选择此选项。 在这种情况下,GUIX 运行时库负责在绘制每个文本字符串前对双向文本进行重新排序。 此解决方案提供运行时性能,并且会对内存产生影响。 必须有充足的动态内存,方可执行双向文本重新排序流程。 此解决方案要求在构建 GUIX 库时定义条件 GX_DYNAMIC_BIDI_TEXT_SUPPORT。 该方案提供两个用于在运行时启用/禁用双向文本支持的 API 函数(gx_system_bidi_text_enable 和 gx_system_bidi_text_disable)

你不应该同时使用 GX_DYNAMIC_BIDI_TEXT_SUPPORT 及配置 GUIX Studio,以按显示顺序生成双向文本。 你必须为双向文本字符串处理选择两种策略中的其一。

内存用量

GUIX 与应用程序一起驻留。 因此,GUIX 的静态内存(或固定内存)使用情况由开发工具决定;例如:编译器、链接器和定位符。 动态内存(或运行时内存)使用情况由应用程序直接控制。

静态内存使用情况

大多数开发工具会将应用程序映像分为五个基本区域:指令、常数、已初始化的数据、未初始化的数据和系统堆栈 。 下图显示了这些内存区域的一种可能的布局:

Memory layout

请务必注意,这只是一个示例。 实际静态内存布局特定于处理器、开发工具、基础硬件和应用程序本身。

指令区域包含程序的所有处理器指令。 此区域通常位于 ROM 中。

常数区包含各种编译的常数,其中 GUIX 包含默认设置和所有应用程序资源(图像、字符串、字体和颜色)。 此外,该区域包含已初始化的数据区域的“最初副本”。 在编译器的初始化过程中,常数区域的这一部分可用于设置 RAM 中已初始化的全局数据。 常数区域通常最大,并且通常位于指令区域后及 ROM 中。

GUIX 像素图和字体通常需要用到大量常数数据存储空间。 这些大型静态数据区域通常保存在 ROM 或 FLASH 中。

在 gx_system.h 文件的未初始化数据区域(作为全局变量)中定义 GUIX 线程堆栈,如下所示

_gx_system_thread_stack[GX_THREAD_STACK_SIZE];

GX_THREAD_STACK_SIZE 是在 gx_port.h 中进行定义的,但应用程序可以通过在 gx_user.h 头文件中定义此符号或通过项目选项或命令行参数来重写定义。 堆栈大小必须足够大,才能处理情况最糟糕的事件处理以及嵌套的绘图调用。

动态内存使用情况

如前所述,动态内存使用情况由应用程序直接控制。 与画布等关联的控制块和内存可以放置在目标内存空间中的任何位置。 这项功能非常重要,因为该功能有助于在运行时轻松利用不同类型的物理内存。

例如,假设目标硬件环境同时包含快速内存和慢速内存。 如果应用程序需要额外性能来执行绘图,则可以将画布内存显式置于高速内存区域,以获得最佳性能。

一些可选的 GUIX 服务和功能需要构建运行时动态内存分配机制,通常称为“堆”。 这些服务和功能是完全可选的,许多 GUIX 应用程序不使用任何堆,也不定义运行时内存分配机制。

如果你打算使用需要运行时内存分配的服务,则必须安装 GUIX 在系统必须动态分配或释放内存时将会调用的函数。 你可以根据个人偏好实现这些函数,这样一来,即使在这种情况下,动态内存池的位置也会由应用程序进行控制。 若要安装对动态内存分配的支持,则应用程序应在程序启动过程中调用 API 服务 gx_system_memory_allocator_set,以定义内存分配和内存释放服务。 有关完整示例,请参阅此 API 的文档。

需要使用运行时内存分配和解除分配服务的 GUIX 服务包括:

  • 将外部存储中的二进制资源加载到 GUIX 运行时环境中。

  • 软件运行时 jpeg 图像解码器。

  • 软件运行时 png 图像解码器。

  • 将文本小组件与 GX_STYLE_TEXT_COPY 结合使用。

  • 运行时像素图重设大小和旋转实用工具函数。

  • 运行时屏幕和小组件控制块分配。

对于较小的应用程序,GUIX 资源通常是作为应用程序映像的一部分进行编译和静态链接,且无需安装二进制资源。 二进制资源允许应用程序在运行时安装从某些存储位置(如 U 盘或 URL)加载的资源(字体、图像、语言)。

运行时 jpeg 和 png 解码器属于可选组件。 大多数 GUIX 应用程序都允许 GUIX Studio 工具预解码所有必需的图像文件,并将其存储为专有 GUIX 像素图数据资源。 如果应用程序需要将运行时 jpeg 和/或 PNG 图像转换为像素图格式,则可使用提供的这些服务完成所有操作。

GX_STYLE_TEXT_COPY 允许用户指定特定的一个或多个小组件保留其自己的动态分配文本的专用副本。 使用此选项要求你在使用前安装内存分配机制。 如果 在创建文本类型小组件时提供此样式标志,则应用程序必须为所有动态创建和分配的文本字符串分配静态存储区。 在这种情况下,不应使用自动变量来保存运行时生成的字符串数据。 如果 GX_STYLE_TEXT_COPY 样式已启用,则可以使用自动变量来保存分配给 GUIX 小组件的字符串数据,因为每个小组件都将创建其自己的分配文本副本。

像素图重设大小和旋转实用工具函数会将最终转换后的像素图作为可用于应用程序的新像素图返回。 若要使用这些服务,则必须有足量的可用动态内存来保存这些运行时生成的像素图数据块。

最后,GUIX 屏幕和小组件的控制块可以静态或动态方式分配。 较小型的应用程序通常会在程序启动过程中创建所有应用程序屏幕,并使用静态分配的控制块。 大型应用程序通常会根据需要动态创建屏幕和子级小组件控件。 指定动态分配控制块的方法为:在 GUIX Studio 的属性视图中选择“运行时分配”复选框,或者在使用标准 API 创建小组件时,通过样式标志 GX_STYLE_DYNAMICALLY_ALLOCATED 传递。 使用动态分配小组件控制块要求使用按如上所述定义的内存分配和释放服务。

GUIX 组件

GUIX API 被划分并组织成数个对应于 GUIX 系统基本组件的基本组。 基本组件包括:

组件 描述
GX_SYSTEM GUIX 系统组件,负责初始化、事件、计时器、字符串表和可视小组件层次结构管理。
GX_CANVAS 绘图区域。 画布可以是硬件帧缓冲区的精简抽象,也可能是纯内存画布。 画布类型由传递到 gx_canvas_create API 函数的参数决定。
GX_CONTEXT 绘图上下文组件。 绘图上下文包含与屏幕、画布、画笔和当前绘图操作的剪辑区域有关的信息。
GX_DISPLAY 提供 API 和驱动程序级别实现,以允许你的应用程序和 GUIX 小组件在画布上执行绘图。 实现 GX_DISPLAY 以使用画布所需的颜色格式在每张画布上正确绘制图形。 GX_DISPLAY 组件还将管理(颜色、字体和像素图)的资源,而这些资源可用于将小组件绘制到与每个显示建立链接的画布。
GX_WIDGET 基本可视小组件对象和关联的 API。 所有 GUIX 小组件类型均基于或派生自基本 GX_WIDGET 类型。
GX_UTILITY 此组中包含用于处理矩形的实用工具函数、用于字符串转换的函数和非 ANSI 数学函数。

除这些基本组件以外,GUIX 还包括库中提供的各类小组件独有的 API。 有关这些 API 的信息,请参阅本用户指南第 4 章(“GUIX 服务说明”)。

GUIX 系统组件

GUIX 系统组件为 UI 应用程序提供了一些全局服务。 这些服务包括:初始化、事件管理、显示管理、资源管理、计时器管理和小组件管理 。 每项服务都对应用程序的所属组织至关重要,下方子章节将针对这些服务作出更为详细的说明。

初始化

GUIX 初始化是通过应用程序调用服务 gx_system_initialize 来完成,其中该服务可能由应用程序从 ThreadX tx_application_define 例程(初始化上下文)或从应用程序线程调用。 Gx_system_initialize 函数可初始化所有全局 GUIX 数据结构,并创建 GUIX 系统互斥、事件队列体、计时器和线程。 Gx_system_initialize 返回后,应用程序便可使用 GUIX

线程处理

在初始化期间创建的内部 GUIX 线程将负责 GUIX 中的大多数处理工作。 此线程中的处理会先完成基础显示驱动程序所需的任何其他初始化。 完成此操作后,GUIX 线程会进入一个循环,内容包括先处理 GUIX 事件队列中存在的所有事件,然后在有需要时刷新屏幕。 屏幕刷新会根据可视内容执行必要的 GUIX 绘制函数,而已经被标记为“脏”则表示需要执行重新绘制。 如果没有任何事件,并且没有任何内容可以在显示时刷新,则 GUIX 线程将挂起并等待下一个 GUIX 事件到达。

RTO 绑定

GUIX 系统组件默认配置为利用 ThreadX 实时操作系统来提供服务(如线程服务、事件队列服务和计时器服务)。 通过使用预处理器指令 GX_DISABLE_THREADX_BINDING 并重新构建 GUIX 库,你可以轻松将 GUIX 移植到其他操作系统。 此操作将删除 GUIX 源代码中的 ThreadX 依赖关系,并允许应用程序开发人员使用目标系统提供的任何 RTOS 来实现所需的操作系统服务。 附录 F - GUIX RTOS 绑定服务将描述在将 GUIX 移植到除 ThreadX 操作系统以外的其他操作系统时所需实现的服务。

多线程安全

GUIX API 可从 GUIX 线程上下文以及其他应用程序线程获得。 应用程序线程可以通过发送和接收事件、访问共享变量以及使用 GUIX API 函数来与 GUIX 线程建立交互。 GUIX 对多线程资源保护使用内部 ThreadX 互斥。 此外,当系统开始执行屏幕刷新操作后,GUIX 将会阻止修改可视小组件的内部结构。 当绘图操作正在进行时,系统将会阻止修改可视对象树的 API,并在屏幕刷新完成后执行释放。

系统计时器

GUIX 将为应用程序提供定期计时器,其通常用于定期更新 GUIX 窗口中显示的数据。 此操作通过 ThreadX 定期计时器实现,该计时器还可用于执行 GUIX 系统级效果(如屏幕淡入/淡出等等)。

应用程序可创建计时器并使用 GUIX 在内部使用的相同计时器设备。 当然,应用程序还可以根据需要直接创建和使用 ThreadX 计时器。 GUIX 计时器的优点是非常易于使用,并预先配置为在 GUIX 事件驱动的处理系统中工作。

若要创建并启动 GUIX 计时器,应用程序应调用函数 gx_system_timer_start。 此函数的参数包括指向调用小组件的指针、计时器 id(允许一个小组件启动多个计时器),以及初始和重新安排超时值。 如果重新安排超时值为 0,则计时器将只运行一次,并且过期后会将其自身从活动计时器列表中删除。

启动后,GUIX 计时器会将 GX_EVENT_TIMEOUT 事件发送给计时器所有者,可以只发送一次或根据计时器的重新安排值定期发送。 GUIX 计时器可通过调用 API 函数 gx_system_timer_stop 来停止

触笔速度配置

GUIX 系统组件会保存与触笔速度跟踪相关的配置信息。 GUIX 会基于触控输入驱动程序生成的 PEN_DOWN 事件的速度和距离,在内部生成 GX_EVENT_VERTICAL_FLICK 和 GX_EVENT_HORIZONTAL_FLICK 事件(如有)。 应用程序可以使用 gx_system_pen_configure API 函数配置触发这些内部生成的事件所需的最小距离和最低速度。

屏幕堆栈

GUIX 系统组件提供与 GUIX 屏幕堆栈相关的服务,其中屏幕堆栈是一项支持虚拟小组件堆栈的可选功能,运行时可通过应用程序在屏幕上推送、弹出和检索内容。 屏幕堆栈非常适合用于管理复杂的菜单系统,其中用户到达菜单系统中各种状态的路径是不同的。 若想在菜单系统中轻松返回到之前的状态,你可以先推送上一个屏幕状态,然后显示新屏幕,并允许新屏幕在当前屏幕关闭时从屏幕堆栈弹出之前的状态。

剪贴板维护

GUIX 支持在运行时执行期间使用复制和粘贴文本的剪贴板。 该项支持由 GUIX 系统组件提供。

脏列表维护

GUIX 会维护一个脏小组件列表,这意味着小组件可视,且因状态变更或重新显示而需要重新绘制。 此脏列表允许 GUIX 执行一次画布刷新操作来反映对 UI 状态作出的所有当前更改,而不是在每次更改 UI 时刷新画布,因此可提高绘图性能。 此外,此脏列表由 GUIX 系统组件维护。

动画控制块池

应用程序通常需要并行执行多个动画序列。 GUIX 会维护可供应用程序进行分配和使用的动画控制块池。 这使得应用程序可以通过静态方式定义这些控制块并使其在不同的时间重复使用,而不是为应用程序可能定义的每个动画定义唯一的动画控制块。 动画控制块池也由 GUIX 系统组件维护。

系统错误处理

GUIX 系统错误处理程序旨在帮助应用程序在 GUIX 内部查找可能更难从 API 角度确定的系统错误。 无论 GUIX 内部何时发生系统错误,系统都将调用内部 _gx_system_error_process 函数。 此函数会保存提供的错误代码并增加检测到的系统错误总数。 系统错误变量的定义如下:

UINT _gx_system_last_error

ULONG _gx_system_error_count

如果 GUIX 应用程序操作反常,查看调试器中的错误计数变量将很有帮助。 如果已完成设置,则有一个解决问题的好方法是在 _gx_system_error_process 函数中设置一个断点,并查看其被调用的时间/所在位置

GUIX 画布组件

画布组件负责所有画布相关处理。 画布实际上是一个虚拟帧缓冲区。 应用程序必须至少创建一张画布,才能生成图形输出。 通常来说,你至少要为系统支持的每个物理显示创建一张画布。

所有 GUIX 绘图都将在画布上发生。 在较为简单或内存有限的系统中,可能只会有一张画布直接链接到可视帧缓冲区,而要求更多内存和更高级图形的系统可能需要多张画布。 使多张画布可用于一个显示可启用屏幕和窗口淡入和淡出效果等功能。 画布可以是以下两种主要类型之一:简单或托管。

简单画布是应用程序使用的一种离屏绘图区域。 GUIX 不会直接使用简单画布执行任何操作,但应用程序可以使用简单画布将复杂绘图呈现到离屏缓冲区,然后在需要时使用此离屏缓冲区来刷新可视画布。

托管画布由 GUIX 自动显示在硬件帧缓冲区内。 为拥有足够内存支持多张托管画布的系统构建合成画布时要用到托管画布。 托管画布的 Z 顺序由 GUIX 维护,并且系统会在所有托管画布上强制执行视图剪辑。

画布与帧缓冲区的不同之处在于,画布更通用。 在内存受限的系统中,可能只有一张画布,而且此画布的内存可能是可视的帧缓冲区内存。 但是,对于支持更高级图形覆盖和多张画布的更复杂系统,每张托管画布都获分配有各自的内存区域,但不同于硬件帧缓冲区内存。 在帧缓冲区刷新或切换操作期间,这些托管画布将呈现到可视帧缓冲区中。

对于支持多个图形层(即多个重叠帧缓冲区)的硬件,应用程序可以使用 gx_canvas_hardware_layer_bind API 将一张或多张画布绑定到硬件图形层。 该服务会通知画布其已链接到特定硬件图形层,并且一旦链接此画布,系统就会尝试利用硬件支持提供画布可见性(即 x_canvas_show、gx_canvas_hide)、画布 alpha 混合(即 gx_canvas_alpha_set)和显示中的画布偏移量 (gx_canvas_offset_set)

对于最简单的单张画布/单个帧缓存器组织之外的其他体系结构,画布的大小由应用程序决定,并且可能不同于帧缓冲区的固定大小。 它也可以是应用程序选择的偏移量。 其他信息(如 Z 顺序)将在画布中进行维护。 画布绘制完成后,驱动程序会将画布的内容显示传输到物理显示。 在部分系统中,如果没有充足的内存可用于单独的画布和帧缓冲内存区域,则画布更新实际上是通过显示驱动程序直接对物理显示进行的。

画布创建

你可以在初始化期间或在执行应用程序线程期间随时创建画布对象。 应用程序可以创建的画布对象数没有限制。 但是,大多数应用程序只会为所有 GUIX 绘图创建一个画布对象。

画布控件块

每个画布对象的特征可在其控制块 GX_CANVAS 中找到,并在 gx_api 中定义。 画布对象所需的内存由应用程序提供,并且可以位于内存中的任何位置。 但是,若要在任何函数的作用域外定义画布对象控制块和绘图区域,最常见的方法便是将其定义为全局结构。

画布 alpha 通道

GUIX 支持多个级别前景色和背景色的 alpha 混合,包括位图 alpha 通道,该通道指定了每个像素的混合比例,其中画笔 alpha 指定画笔的混合比例(16 bpp 及更高的颜色深度),画布 alpha 则用于指定覆盖画布的混合比率。

如果要组合多张画布并在帧缓冲区内显示,可使用画布的 alpha 值。 如果画布 Z 顺序使画布位于其他画布上方,则可以将画布 alpha 值设置为混合位于其后的画布值。 快速修改画布的 alpha 值可用于提供“淡入”屏幕过渡效果,但可以通过多种方式使用画布 alpha。

如果使用 gx_canvas_hardware_layer_bind() 将画布绑定到硬件图形层,GUIX 将尝试使用硬件支持实现画布 alpha 值混合,将与混合重叠画布关联的软件开支降到最低。

Alpha 值的范围为 0 到 255,其中值为 0 表示完全透明,值大于 0 表示逐渐不透明的画布 alpha 值,仅支持在 16 bpp 及更高颜色深度上运行的屏幕驱动程序(除非系统可提供用于画布混合的硬件帮助)。

画布偏移量

画布可以通过调用 gx_canvas_offset_set API 服务在可视帧缓冲区内偏移。 画布偏移量通常用于实现精灵动画效果。 如果通过调用 gx_canvas_hardware_layer_bind API 函数将画布绑定到硬件图形层,GUIX 将尝试使用硬件支持实现画布偏移量更改,从而将与切换画布位置相关的软件开支降到最低

画布绘图

GUIX 画布组件提供应用程序的完整绘图 API。 在可以调用 gx_canvas_line_draw 或 gx_canvas_pixelmap_draw 等绘图 API 之前,必须通过调用 gx_canvas_drawing_initiate API 函数打开目标画布进行绘图。 此函数会准备画布进行绘图并创建“绘图上下文”

呈现给画布的绘图 API(如 gx_canvas_line_draw 或 gx_canvas_text_draw)使用在当前绘图上下文画笔中找到的参数来定义线条样式、宽度和颜色。 要修改这些画笔参数,可通过调用 gx_context_brush_define、gx_context_brush_set、gx_context_brush_style_set,以及通过调用 gx_canvas_drawing_initiate 建立绘图上下文后出现的类似 API 函数

当 GUIX 将窗口和小组件绘制函数作为延迟画布刷新操作的一部分进行调用时,系统会在调用小组件绘制函数之前为绘图打开目标画布。 因此,你在打开目标画布时无需使用标准小组件绘图函数,因为相关操作已经执行完毕。

在某些情况下,应用程序可能需要强制在画布上即时绘图。 这种情况下,应用程序可执行以下步骤。

  1. 调用 gx_canvas_drawing_initiate API 函数,传入目标画布和应用程序想要在画布内绘图的矩形。

  2. 调用任意数量的画布绘图 API 来完成所需的绘图。

  3. 调用 gx_canvas_drawing_complete API 函数来通知绘图已完成。 这会将画布刷新到可视帧缓冲区,并/或触发缓冲区切换操作,具体取决于系统内存体系结构。

绘图 API

GUIX 需要若干主要绘图基元来绘制屏幕上的所有视觉对象。 这些绘图 API 也可由应用程序软件调用,通常作为自定义小组件绘图函数的一部分。 这些 GUIX 画布绘图 API 可执行参数验证和剪裁,然后将剪切的绘图坐标向下传递到硬件和特定于颜色格式的绘图实现的显示驱动程序。 这些绘图 API 函数的定义如下。

  • gx_canvas_alpha_set
  • gx_canvas_arc_draw
  • gx_canvas_block_move
  • gx_canvas_circle_draw
  • gx_canvas_ellipse_draw
  • gx_canvas_glyphs_draw
  • gx_canvas_hardware_layer_bind
  • gx_canvas_hide
  • gx_canvas_line_draw
  • gx_canvas_offset_set
  • gx_canvas_pie_draw
  • gx_canvas_pixel_draw
  • gx_canvas_pixelmap_blend
  • gx_canvas_pixelmap_rotate
  • gx_canvas_pixelmap_tile
  • gx_canvas_polygon_draw
  • gx_canvas_rectangle_draw
  • gx_canvas_rotated_text_draw
  • gx_canvas_shift
  • gx_canvas_show
  • gx_canvas_text_draw

绘图 API 通过 GUIX 画布 API 调用,所有绘图都使用 gx_canvas_* API 函数完成。 使用当前绘图上下文中的当前画笔完成绘制。 上文任何形状绘图函数都可以按照当前画笔的定义描画轮廓、填充纯色或填充像素图。 若要修改形状轮廓宽度、颜色或填充,请使用 gx_context_brush_* API 函数定义当前绘图上下文内的画笔。

上文应用程序级别的绘图 API 不会在画布上进行实际绘画,而是会在调用显示驱动程序级别的绘图函数前验证调用方的参数。 驱动程序级别的绘制函数实际上会将像素数据写入画布内存。

GUIX 为各种颜色深度提供常用或通用显示驱动程序图形功能,包括 1、2、4、8、16、24 和 32 位/像素 (bpp)。 在某些情况下,默认软件绘图实现由提供 2D 绘图加速器的硬件目标的硬件加速实现替换。

颜色深度

GUIX 支持最大为 32-bpp 的颜色深度以及单色和灰度。 颜色深度支持的类型在很大程度上取决于基础物理显示的功能,并且还会影响画布绘图区域所需的内存量。 下方列出了颜色深度支持以及该颜色深度内变量的简要说明。

颜色格式 说明
1 位(单色) 1 位/像素(压缩格式)。
2 位(灰度) 4 个灰度级,每字节压缩四个像素。
4 位(灰度) 16 个灰度级,每字节压缩四个像素。
4 位(颜色) VGA 格式平面内存组织。
8 位(灰度) 256 个灰度级
8 位(调色板模式) 每个像素 1 字节(用作调色板索引)
8 位(r:g:b 模式) 2:3:2 r:g:b 格式(不太常用)。
16 位 每个像素需要两个字节。 字节顺序可以是 r:g:b 或 b:g:r。 通常为 5:6:5 结构,但也可以是 5:5:5 结构或 4:4:4:4 a:r:g:b 结构。
24 位 每个像素都需要 3(压缩格式)或 4(解包格式)个字节。 可按硬件要求的 r:g:b 或 b:g:r 字节顺序排列。
32 位 每个像素需要 4 字节(带 alpha 通道)。 字节顺序可以是 a:r:g:b 或 b:g:r:a 并由硬件确定。

鼠标支持

GUIX 支持在任何所需画布上绘制鼠标光标。 鼠标光标可以在软件中绘制,也可受硬件光标覆盖的支持。 无论是哪种情况,无论是使用软件还是硬件鼠标光标绘图,提供给与鼠标光标支持相关的应用程序都是相同的。

只有在构建 GUIX 库前于 gx_user.h 标头文件中定义 #define GX_MOUSE_SUPPORT,GUIX 鼠标支持才会启用。

应用程序必须使用 gx_canvas_mouse_define API 函数定义鼠标光标和热点。 此 API 接受指向应在其上绘制光标图像的画布的指针,以及指向 GX_MOUSE_CURSOR_INFO 结构的指针,该结构可定义鼠标光标图像和相对于图像左上角的鼠标图像热点。

GUIX 显示组件

显示组件是 GUIX 中的基础元素,因其管理着所有显示对象的处理,而这些显示对象本身包含又一个或多个画布、小组件和窗口。 显示组件还可通过一系列函数指针与每个显示关联的基础硬件屏幕驱动程序建立交互。

显示创建

可以在初始化期间或在执行应用程序线程期间随时创建显示对象。 应用程序通常会创建一个显示对象来管理每个物理屏幕。 如果你已使用 GUIX Studio 定义应用程序,并且物理显示可用,你便可使用 gx_studio_display_configure API 函数创建并初始化每个显示。

显示控制块

每个显示对象的特征都位于其控制块 GX_DISPLAY 中,并在 gx_api.h 中定义。 显示对象所需的内存由应用程序提供,并且可以位于内存中的任何位置。 但是,在任何函数的作用域外部定义显示控制块最常见的方法是使其成为全局结构。

资源管理

资源是应用程序所需的 UI 组件,但不是应用程序代码。 资源是应用程序数据,通常以静态形式定义。 资源类型包括像素图、字体、颜色和字符串。 你可以随时变更这些资源,无需更改任何应用程序软件。 请务必保留与应用程序软件分离之资源的存储和引用,以便在不变更应用程序代码的情况下更改 UI 外观,原因是对应用程序软件所做的更改通常需要对该软件进行相关的重新测试和验证。

GUIX 显示模块为依赖于显示的颜色深度和格式的所有资源提供资源管理功能。 这些功能包括维护活动像素图表、活动字体表和活动颜色表。 字符串表资源由 GUIX 系统模块维护,因为字符串资源通常不需要根据颜色深度和格式作出更改。

应用程序软件按资源 ID 引用资源,此 ID 指对应资源表中的索引。 此操作允许更改表,例如产品从“日间模式”更改为“夜间模式”时,颜色表可能发生变化,但颜色 ID 值会保持不变。

你的应用程序资源将由 GUIX Studio 应用程序写入一份(或一套)资源文件。 当你创建新的 GUIX Studio 项目时,系统会自动提供默认颜色、像素图和字体,但你可以在定义应用程序外观时轻松替换这些默认值。

需要注意的是,系统无法将颜色、字体和像素图的资源 ID 解析为实际的颜色、字体或像素图值(除非活动显示组件已知相关值)。 由于 GUIX 体系结构支持多个活动的显示,因此,在小组件及其关联的资源 ID 可以解析为特定显示时,只能将资源 ID 解析为资源值。 此属性称为动态绑定。 在一个显示上使用时,属性(如文本颜色)的资源 ID(如资源 ID GX_COLOR_ID_TEXT)可能会解析为 16 位(白色)R:G:B 值,但在其他显示器上使用时,相同的颜色 ID 可能会解析为单色黑色值。

在实践中,将资源 ID 的动态绑定到资源值是指应用程序软件和 GUIX 内部组件应最常仅将资源 ID 解析为活动绘图上下文内的资源值。 活动的绘图上下文可指定当前活动的显示,这允许 GUIX 将每个资源 ID 解析为特定资源值。 如果应用程序软件需要在绘图上下文外部查找特定资源值,则也可以为可视小组件执行此操作。 可视小组件将链接到根窗口,该窗口还可用于解析活动画布并显示该小组件。

如果小组件已创建但尚未显示(例如,尚未链接到任何根窗口或其他可视父项),则与该小组件关联的任何资源 ID 都不能解析为特定资源值,只能直接索引分配给特定显示的资源表。 应用程序软件可安全实现对特定资源表的这种直接访问,但不能在内部 GUIX 库软件中完成此操作。

小组件默认值

GUIX 显示组件还提供各种小组件属性的默认定义。 除非应用程序另有指定,否则便会使用这些系统属性创建小组件/窗口。 这些系统属性主要包含系统资源表中维护的字体、颜色和位图。 默认滚动条外观的其他属性也由 GUIX 显示组件维护。

默认颜色设置由分配给每个显示的颜色表和预定义的默认颜色 ID 定义。 这些默认颜色 ID 包括以下各项。

颜色 ID 描述
GX_COLOR_ID_CANVAS 默认画布(即显示背景)颜色
GX_COLOR_ID_WIDGET_FILL 默认小组件填充颜色
GX_COLOR_ID_WINDOW_FILL 默认窗口填充颜色
GX_COLOR_ID_DISABLED_FILL 默认禁用的小组件填充颜色
GX_COLOR_ID_DEFAULT_BORDER 默认小组件边框颜色
GX_COLOR_ID_WINDOW_BORDER 默认窗口边框颜色
GX_COLOR_ID_TEXT 默认文本颜色
GX_COLOR_ID_SELECTED_TEXT 默认选定文本颜色
GX_COLOR_ID_DISABLED_TEXT 默认禁用的文本颜色
GX_COLOR_ID_SELECTED_TEXT_FILL 默认选定文本填充颜色
GX_COLOR_ID_READONLY_TEXT 默认的只读文本颜色
GX_COLOR_ID_READONLY_FILL 默认只读文本填充颜色
GX_COLOR_ID_SCROLL_FILL 滚动条填充颜色
GX_COLOR_ID_SCROLL_BUTTON 滚动条按钮填充颜色
GX_COLOR_ID_SHADOW 默认阴影颜色
GX_COLOR_ID_SHINE 默认突出显示颜色
GX_COLOR_ID_BUTTON_BORDER 按钮小组件边框颜色
GX_COLOR_ID_BUTTON_UPPER 按钮小组件上部填充颜色
GX_COLOR_ID_BUTTON_LOWER 按钮小组件下部填充颜色
GX_COLOR_ID_BUTTON_TEXT 按钮小组件文本颜色
GX_COLOR_ID_TEXT_INPUT_TEXT 文本输入小组件文本颜色
GX_COLOR_ID_TEXT_INPUT_FILL 文本输入填充颜色
GX_COLOR_ID_SLIDER_TICK 用于绘制滑块刻度线的颜色。
GX_COLOR_ID_SLIDER_GROOVE_BOTTOM 用于绘制滑块沟的颜色
GX_COLOR_ID_SLIDER_NEEDLE_OUTLINE 用于绘制针轮廓的颜色
GX_COLOR_ID_SLIDER_NEEDLE_FILL 用于填充滑块针的颜色
GX_COLOR_ID_SLIDER_NEEDLE_LINE1 用于绘制针高光的颜色
GX_COLOR_ID_SLIDER_NEEDLE_LINE2 用于绘制针阴影的颜色

这些颜色 ID 值可映射到指定给每个显示的颜色表定义的特定颜色值。 你可以通过调用 gx_display_color_table_set API 函数,将这些默认值更改为一个显示组。 如果你使用的是 GUIX Studio,则当应用程序调用 gx_studio_display_configure 函数时,系统会自动初始化“显示颜色”表

GUIX 显示组件还会保留默认字体表。 默认字体表定义每个小组件类型使用的字体,除非应用程序专门指定该字体。 预定义的显示字体表 ID 包含以下值。

字体 ID 说明
GX_FONT_ID_DEFAULT 未定义任何特定字体时使用的默认字体
GX_FONT_ID_BUTTON 用于按钮上所有文本的默认字体
GX_FONT_ID_TEXT_INPUT 用于文本编辑字段的默认字体

任何文本类型小组件使用的字体 ID 都可以通过使用为每个文本相关小组件类型提供的 gx_<widget_type>_font_set API 来重新分配。 通过调用 gx_display_font_table_set API 函数,你可以重新分配整个字体表。

滚动条外观

GUIX 显示屏还会保留该显示的默认滚动条外观设置。 这些设置由下文定义的 GX_SCROLLBAR_APPEARANCE 结构予以定义。 GUIX 显示将维护一个垂直滚动条的滚动条外观结构以及另一个用于水平滚动条的结构。 应用程序可以通过初始化 GX_SCROLLBAR_APPEARANCE 结构并调用 API 函数 gx_display_scroll_appearance_set 来修改任何显示的默认滚动条外观

typedef struct GX_SCROLLBAR_APPEARANCE_STRUCT
{
    GX_VALUE       gx_scroll_width;
    GX_VALUE       gx_scroll_thumb_width;
    GX_VALUE       gx_scroll_thumb_travel_min;
    GX_VALUE       gx_scroll_thumb_travel_max;
    GX_UBYTE       gx_scroll_thumb_border_style;
    GX_RESOURCE_ID gx_scroll_fill_pixelmap;
    GX_RESOURCE_ID gx_scroll_thumb_pixelmap;
    GX_RESOURCE_ID gx_scroll_up_pixelmap;
    GX_RESOURCE_ID gx_scroll_down_pixelmap;
    GX_RESOURCE_ID gx_scroll_thumb_color;
    GX_RESOURCE_ID gx_scroll_thumb_border_color;
    GX_RESOURCE_ID gx_scroll_button_color;
} GX_SCROLLBAR_APPEARANCE;
GX_SCROLLBAR_APPEARANCE 结构成员 描述
gx_scroll_width 垂直滚动条的宽度或水平滚动条的高度(以像素为单位)。
gx_scroll_thumb_width 电梯和结束按钮的宽度(以像素为单位)。
gx_scroll_thumb_travel_max 从滚动条的末端到最大缩略图按钮点的偏移量。
gx_scroll_fill_pixelmap 用于填充滚动背景的像素图。
gx_scroll_thumb_pixelmap 用于绘制滚动块按钮的像素图。
gx_scroll_up_pixelmap 用于绘制向上滚动按钮的像素图。
gx_scroll_down_pixelmap 用于绘制向下滚动按钮的像素图。
gx_scroll_fill_color 用于填充滚动条背景的颜色 ID。
gx_scroll_button_color 用于填充滚动条滚动块按钮的颜色 ID。

除了字体、颜色和样式的这些默认设置外,应用程序还可以视情况和需要使用每个小组件类型提供的 API 指定任何参数。

外观和主题

外观允许 GUIX 小组件和窗口轻松更改其基本外观,即更改某个位置的“外观”将会改变所有关联小组件和窗口的基本外观。

重新调整 GUIX 应用程序的外观要求你向 GUIX 显示资源表提供新的颜色、字体和像素图表。 由于所有 GUIX 小组件都按资源 ID 引用其颜色、位图或字体,因此,提供新的资源表会自动使所有 GUIX 小组件在将其自身绘制到所需的显示时开始使用新颜色和新像素图。

一组新的字体、颜色和像素图,旨在组合使用以提供引人注目的外观,可称为“主题”。 “主题”可定义一组资源表以及其中每个资源表的大小。 你可以使用 GUIX Studio 应用程序为任何显示定义任意数量的主题。 必须将起始主题索引传递到 GUIX Studio 生成的函数 gx_studio_display_configure 中,该函数会将初始主题安装到创建的显示中。 你可以通过调用函数 gx_display_theme_install 来随时更改任何显示的活动主题

根窗口

应用程序每创建一张可视画布后,还必须为该画布创建一个根窗口。 此特殊窗口基本上可用作所有顶级应用程序窗口和小组件的容器。 根窗口会绘制画布背景,而且由于根窗口派生自 GX_WINDOW 类别,因此根窗口也可以包含墙纸。 若要更改显示或画布的背景色,只需更改附加到画布根窗口的填充颜色。

如果你使用 GUIX Studio 生成的名为 gx_studio_display_configure 的函数来配置显示,则系统将为你创建此初始化函数的一部分的画布和根窗口

抗锯齿

抗锯齿是 GUIX 中的一项可选功能,用于平滑线条、曲线和字体。 仅当使用 16 bpp 或更高颜色深度的显示驱动程序运行时,系统才支持使用抗锯齿。

通过在活动画笔中设置 GX_BRUSH_ALIAS 闪存来启用抗锯齿线条绘图。 这适用于直接绘制的线条以及绘制为多边形或圆形的边框线条。

你可以通过使用 GUIX studio 应用程序生成的抗锯齿字体启用抗锯齿文本绘制。 你可以在创建字体时指定字体是否应生成为抗锯齿或二进制。 GUIX 中的抗锯齿字体每像素使用 16 个透明级。

剪裁

剪裁由 GUIX 显示组件内部支持,并在窗口和小组件层上由 GUIX 小组件维护的父子体系结构支持。 不允许在该小组件区域外绘制任何窗口或小组件,并且不允许小组件在该小组件父区域的外部绘制。

这也会阻止小组件在画布内存之外的像素坐标绘制,并且可能会导致内存损坏或系统故障。 不允许小组件在小组件区域、小组件父区域或画布范围之外进行绘制。

此外,小组件只能绘制到之前标记为“脏”的区域。 这会阻止绘制整个窗口,例如仅显示窗口一角。 只有实际需要刷新的部分窗口被标记为“脏”,因此“窗口绘制”功能仅会在脏区域中刷新像素。

GUIX 显示组件会在调用驱动程序级别的绘图函数之前强制执行这些剪裁算法。

视图

GUIX 一律为每个根窗口以及根窗口的每个子窗口维护一组视图。 视图指运行时确定的动态剪裁区域,会随窗口位置和 Z 顺序的改变发生变化。 GUIX 使用视图阻止背景中的窗口或小组件在前台的窗口或小组件顶部绘图。 视图会强制执行 Z 顺序规则。 此外,视图对效率很重要,因其会阻止背景中的窗口绘制到不可视画布的任何区域。 如果一个窗口完全被另一个窗口覆盖,则不允许涵盖的窗口全部绘制到画布上,即使正在尝试执行此操作也不可。

显示驱动程序接口

GUIX 显示驱动程序负责与基础物理屏幕的所有交互。 显示驱动程序具有三个基本函数:初始化、绘图和帧缓冲区显示。 初始化负责准备物理显示硬件,通知 GUIX 物理显示硬件的属性,并通知 GUIX 应该使用哪些特定的绘制功能。 主显示器驱动程序初始化从 GUIX gx_display_create 函数调用。 此外,GUIX 线程还将从线程上下文调用辅助显示驱动程序初始化。 仅当驱动程序在初始化过程中需要 RTOS 服务时,才需要此辅助显示驱动程序,例如设备中断或 tx_thread_sleep 在执行初始化步骤的过程中发出延迟请求

初始化完成后,显示驱动程序将负责可以在物理显示硬件中完成的任何直接绘图。 最后,显示驱动程序负责显示帧缓冲区。

GUIX 小组件组件

GUIX 小组件为可视图形元素。 有些 GUIX 组件不可视,例如计时器和数学实用函数。 但是,所有可视组件都派生自基本 GUIX 小组件组件。 GUIX 小组件是 GUIX 显示的主要构建基块,而且所有其他图形元素都派生自基本小组件功能。

GUIX 小组件以面向对象的方式实现,完全支持继承。 此操作通过使用 ANSI C 实现,这可以尽可能降低内存和处理要求。 当我们说出一个特定小组件(如 GX_BUTTON)派生自另一个小组件时(如基本 GX_WIDGET),这表示 GX_BUTTON 控制结构包含 GX_WIDGET的所有成员变量和函数指针,以及还有一些特定于 GX_BUTTON 的变量。 GUIX 以这种方式构建多层小组件,因此更复杂的小组件始终是基于更简单的小组件构建而成。 利用这种分层的派生模型,你可以更轻松地了解用于修改小组件参数的 API。 如果要修改按钮颜色,请使用用于修改小组件颜色的同一个 API,即 gx_widget_fill_color_set

使用树状结构列表将子级小组件链接到其父级小组件,以父子方式维护可视小组件的组织。 子元素从其父项继承特征,如可以在其上绘制的视图和其所绘制的画布。 子级小组件可能有其自己的子级小组件,并再次从父级小组件继承各种特征。 你可以通过各种 GUIX API 调用来显式重定义任何小组件的特征。

小组件创建

你可以在初始化期间或在执行应用程序线程期间随时创建小组件对象。 应用程序可以创建的小组件对象数没有限制。 在目标硬件的内存限制内,任何小组件可能包含的子级数量没有限制。

每个小组件类型都有其自己的 create 函数,例如 gx_button_create 或 gx_prompt_create。 这些函数的前三个参数始终相同,即指向小组件控件结构的指针、指向小组件名称的字符串指针和指向小组件父项的指针。 每个 create 函数可以包含任意数量的附加参数,具体取决于该特定小组件类型的要求。

小组件控制块

每个小组件对象的特征都位于其控制块 GX_WIDGET 中,并在 gx_api.h 中定义。 小组件对象所需的内存由应用程序提供,并且可以位于内存中的任何位置。 不过,在任何函数的作用域外部定义小组件对象控制块最常见的方法是使其成为全局结构。 如果你使用的是 GUIX Studio,则可以在 Studio 生成的规范文件中静态分配小组件控制块,也可以通过应用程序对其进行动态分配。

动态小组件控制块分配和取消分配

如果你使用的是动态控制块分配,则需要定义两个 GUIX 在分配和释放小组件控制块所需内存时使用的函数。 内存管理的函数通过 gx_system_memory_allocator_set API 函数传递到 GUIX 系统组件。 此函数允许你将两个函数指针传递到 GUIX 中:第一个是指向内存分配函数的指针,第二个是指向内存释放函数的指针。 大多数情况下,你可以使用 ThreadX 字节池实现这些功能,但 GUIX 的设计可让你按照喜欢的任何方式实现动态内存管理。

当你在 Studio 小组件属性字段中选择“动态分配”选项后,Studio 生成的应用程序规范文件即会最常采用动态小组件分配。 不过,你也可以在应用程序中使用动态控制块分配。 如果是在应用程序中使用动态控制块分配,则应调用 gx_widget_allocate API 函数来分配小组件控制块。 接下来,在创建小组件时,请确保将 GX_WIDGET_STYLE_DYNAMICALLY_ALLOCATED 样式标志(连同任何其他所需的样式标志一起)传递给小组件 create 函数。 此标志可用于在小组件状态字段中将小组件标记为“动态分配”。 如果在使用 gx_widget_delete 后删除小组件,GUIX 将检查此状态字段,并自动调用内存释放分配器函数以确保未发生内存泄漏问题。

重要

使用动态分配的控制块创建的小组件必须使用 GX_WIDGET_STYLE_DYNAMICALLY_ALLOCATED 样式标志来创建,以防内存丢失。

类型

GUIX 提供样式丰富、功能齐全的内置小组件集。 如前所述,所有专用小组件都派生自基本小组件。 下面是 GUIX 中内置小组件的列表:

GX_TYPE_WIDGET

GX_TYPE_BUTTON

GX_TYPE_TEXT_BUTTON

GX_TYPE_MULTI_LINE_TEXT_BUTTON

GX_TYPE_RADIO_BUTTON

GX_TYPE_CHECKBOX

GX_TYPE_PIXELMAP_BUTTON

GX_TYPE_ICON_BUTTON

GX_TYPE_ICON

GX_TYPE_SPRITE

GX_TYPE_SLIDER

GX_TYPE_PIXELMAP_SLIDER

GX_TYPE_VERTICAL_SCROLL

GX_TYPE_HORIZONTAL_SCROLL

GX_TYPE_PROGRESS_BAR

GX_TYPE_PROMPT

GX_TYPE_NUMERIC_PROMPT

GX_TYPE_PIXELMAP_PROMPT

GX_TYPE_NUMERIC_PIXELMAP_PROMPT

GX_TYPE_SINGLE_LINE_TEXT_INPUT

GX_TYPE_MULTI_LINE_TEXT_VIEW

GX_TYPE_MULTI_LINE_TEXT_INPUT

GX_TYPE_WINDOW

GX_TYPE_ROOT_WINDOW

GX_TYPE_VERTICAL_LIST

GX_TYPE_HORIZONTAL_LIST

GX_TYPE_POPUP_LIST

GX_TYPE_DROP_LIST

GX_TYPE_LINE_CHART

GX_TYPE_DIALOG

GX_TYPE_KEYBOARD

GX_TYPE_SCROLL_WHEEL

GX_TYPE_TEXT_SCROLL_WHEEL

GX_TYPE_STRING_SCROLL_WHEEL

GX_TYPE_NUMERIC_SCROLL_WHEEL

GX_TYPE_CIRCULAR_GAUGE

GX_TYPE_RADIAL_PROGRESS_BAR

GX_TYPE_RADIAL_SLIDER

GX_TYPE_MENU_LIST

GX_TYPE_MENU

GX_TYPE_ACCORDION_MENU

GX_TYPE_TREE_VIEW

样式

小组件样式包含如上文所示的边框属性(凸起、凹陷、细、粗或没有任何边框)以及特定小组件类型的属性。 小组件样式标志提供修改任何小组件外观的最简单方法。 初始小组件样式始终是传递给小组件类型特定 create 函数的参数。

颜色

小组件可使用系统颜色表中定义的颜色自行完成绘制。 颜色 ID 旨在定义画布背景、默认小组件填充颜色、按钮填充颜色、文本小组件填充颜色、窗口填充颜色和多个其他默认颜色值。 此外,在填充窗口客户端时,GX_WINDOW 对象支持显示位图或墙纸。

更改默认配色方案最简单的方法是使用 GUIX Studio 创建或定义满足要求的配色方案。 你还可以通过创建 GX_COLOR 值的数组并调用 gx_system_color_table_set API 函数来手动定义配色方案。

事件通知

GUIX 事件指对一个或多个小组件发出执行特定操作和通知的请求,以通知小组件用户输入和内部系统状态更改内容。 例如,当小组件获得焦点时,系统会通过 gx_system_event_send API 服务将 GX_EVENT_FOCUS_GAINED 发送到该小组件

事件通过 GUIX 事件队列传递,而且每个事件都是 GX_EVENT 数据结构的实例。 GX_EVENT 数据结构在 gx_api.h 中定义,但结构中最重要的字段是 gx_event_type、gx_event_sender、gx_event_target 和 gx_event_payload

gx_event_type 字段可用于标识特定事件类。 事件类型指示这是否为(例如)GX_EVENT_PEN_DOWN 事件或 GX_EVENT_TIMER 事件。 gx_event_payload 是多个数据字段的联合,其不适用于所有事件类型。 在检查其他事件数据字段之前,先使用事件类型字段。

如果事件为子级小组件通知,则 gx_event_sender 字段包含生成事件的小组件 ID。

gx_event_target 字段可用于将用户定义的事件路由到特定的窗口或小组件。 如果要将事件发送到特定窗口,你应该在生成事件时为窗口指定唯一的 ID 值(以便能够明显识别),并在生成事件时于 gx_event_target 字段中放置窗口 ID 值。 如果你不知道目标 ID,或者只是想要将事件路由到具有输入焦点的小组件,请确保将 gx_event_target 字段设置为 0。

最后,gx_event_payload 字段即为各种数据类型的联合。 对于 GX_EVENT_PEN_DOWN 和 GX_EVENT_PEN_UP 事件,gx_event_pointdata 字段包含触笔位置的 x,y 像素坐标。 对于计时器事件,gx_event_timer_id 字段包含过期计时器的 ID。 其他事件类型则使用其他负载数据字段。 预定义事件类型及其有效负载字段的完整列表已在“附录 E - GUIX 事件说明”中定义。

应用程序还可以添加自己的自定义事件,并从常量 GX_FIRST_APP_EVENT 后以数字开始表示。 此常量后的所有事件号都保留给应用程序使用。 当然,应用程序的小组件事件处理程序必须要处理此类应用程序事件。

事件处理

每个小组件都有一个默认的小组件事件处理函数,名称为 gx_<widget-type>_event_process。 在大多数情况下,应用程序无需担心任何给定小组件的事件处理。 但是,在应用程序需要自定义或补充事件处理的情况下,其可以通过 GUIX API gx_widget_event_process_set 覆盖默认处理函数。 此函数用 API 中指定的事件函数处理函数来覆盖默认事件处理函数。

重要

只需直接调用默认处理 gx_widget_event_process,应用程序事件处理函数便可利用(即不重复处理)默认处理

只能从内部 GUIX 系统线程的上下文中调用事件处理。 如果采用这种方式,则处理事件处理的堆栈要求将仅适用于 GUIX 线程。

实现自定义事件处理(示例)

你可以为 GUIX 系统中的任何小组件或窗口提供自己的事件处理函数。 如果要创建自己的自定义小组件类型,你通常要在小组件创建函数中安装自定义事件处理程序。 如果只是扩展或修改现有小组件或窗口的操作,则可以在创建小组件或窗口后调用 gx_widget_event_process_set API 函数。 你几乎始终要为顶层窗口(也称为屏幕)提供自己的事件处理,以便处理由子控件生成的事件。 处理屏幕子控件生成的事件是向 GUIX 应用程序添加功能的主要方式。

例如,假设你定义了一个名为“main_menu”的顶层屏幕。 此屏幕可能是使用 GUIX Studio 定义,也可能是在应用程序代码中创建。 如果是在 GUIX Studio 中定义屏幕,你只需在该屏幕的“Studio 属性”字段中键入事件处理程序的名称,届时 Studio 生成的规范代码将自动安装事件处理程序。 在这种情况下,我们将调用自定义事件处理程序 main_menu_event_handler 并应按照如下方式进行编码

int main_menu_item; /* example: variable to keep track of selected item */

UINT main_menu_event_handler(GX_WINDOW *main_screen, GX_EVENT *event_ptr)
{
    UINT status = GX_SUCCESS;

    switch(event_ptr->gx_event_type)
    {
    /* this is an example for catching events from a child button */
    case GX_SIGNAL(IDB_CHILD_BUTTON, GX_EVENT_CLICKED):
        /* insert your button handler code here */
        break;

    case GX_EVENT_SHOW:
        /* add functionality to the show event handler */
        /* first, do default processing */
        status = gx_window_event_process(main_screen, event_ptr); /* note 1 */

        /* now add my own processing */
        main_menu_item = 0;
        break;

    default:
        /* pass all other events to base processing function */
        status = gx_window_event_process(main_screen, event_ptr); /* note 1 */
        break;
    }
    return status;
}

在上方示例中,请务必注意,对于 GX_EVENT_SHOW 等系统事件(内部生成的用于通知小部件状态更改情况的事件),应用程序必须将这些事件传递给基本小部件事件处理函数,以确保系统正常处理事件。 之后,应用程序可以根据需要添加其他逻辑。 应用程序未处理的所有事件(上述默认情况下)也应传递给基本事件处理函数。 此示例为基于 GX_WINDOW 的顶层屏幕,因此默认事件处理函数为 gx_window_event_process。

绘图函数

所有小组件绘图都与事件处理分开执行。 从 CPU 周期角度而言,这种方式反而更为高效,因为绘图通常是一笔昂贵的开销。 通过实现延迟绘制算法,所有未完成的事件和关联的显示更改都可以在完成任何绘制之前处理完毕,从而可以避免浪费绘图操作。 与事件处理类似,大多数小组件都有一个默认小组件图形函数,名为 gx_<widget-type>_draw,其中 xxx 指小组件类型。 在大多数情况下,应用程序无需担心任何给定小组件的绘图功能。 但是,在应用程序需要自定义或补充绘图的情况下,其可以通过 GUIX API gx_widget_draw_set 覆盖默认的绘图函数。 此函数允许应用程序为任何小组件提供自己的自定义绘图函数。 这样一来,应用程序便可定义整个新小组件的类型。

重要

只需直接调用从覆盖的绘画函数中调用默认绘画,应用程序绘画函数便可利用(即不重复处理)默认绘画。

只能从内部 GUIX 系统线程的上下文中调用小部件绘图。 如果采用这种方式,则执行绘图的计时和堆栈要求将仅适用于 GUIX 线程。

实现自定义绘图(示例)

任何小组件的绘制函数都通过间接函数指针引用,该指针是 GX_WIDGET 控制块的成员。 如果使用 GUIX Studio 来定义小组件,则只需在小组件属性的“绘图函数”参数中键入函数名称,即可安装你自己的函数指针。系统在创建小组件时,Studio 会为你安装函数指针。 如果在应用程序代码中创建小组件,则必须在创建小组件后使用 gx_widget_draw_set API 函数安装自定义绘图函数

在本示例中,我们先假设你想要自定义一个按钮的外观。 这个按钮看上去和 GX_TEXT_BUTTON 非常相似,但当你按下该按钮时,我们会在按钮中间偏右的位置另外绘制一张小的“LED_ON”绿色位图;未按下该按钮时,则另外绘制一张小的“LED_OFF”位图。 我们想要创建一个如下图所示的按钮。

Screenshot of the green button for On. 自定义按钮“开”

Screenshot of the red button for Off. 自定义按钮“关”

在这种情况下,我们会编写一个与以下内容相似的按钮绘图函数。

UINT my_button_draw(GX_TEXT_BUTTON *button)
{
    GX_PIXELMAP *map;
    ULONG button_style;
    INT xpos;
    INT ypos;

    /* first, do the normal text button drawing */
    gx_text_button_draw(button);

    /* now add our extra pixelmap */

    gx_widget_style_get(button, &button_style);

    if (button_style & GX_STYLE_BUTTON_PUSHED)
    {
        /* use the ON pixelmap */
        gx_context_pixelmap_get(GX_PIXELMAP_ID_LED_ON, &map);
    }
    else
    {
        /* use the OFF pixelmap */
        gx_context_pixelmap_get(GX_PIXELMAP_ID_LED_OFF, &map);
    }
    if (map)
    {
        /* draw it 20 pixels in from right edge */
        xpos = button->gx_widget_size.gx_rectangle_right;
        xpos -= map->gx_pixelmap_width + 20;

        /* and draw 10 pixels from the top edge */
        ypos = button->gx_widget_size.gx_rectangle_top + 10;

        /* draw the extra pixelmap on top of the button */
        gx_canvas_pixelmap_draw(xpos, ypos, map);
    }
}

GUIX 绘图上下文组件

在运行时,系统将在 GUIX 执行每个画布刷新操作时动态创建绘图上下文。 绘图上下文将用于联系执行当前绘画操作时使用的画布、屏幕驱动程序和触笔。

绘图上下文由 GX_DRAW_CONTEXT 结构定义。 此结构包含用于定义当前绘图操作的剪裁和视图、定义当前画布和定义当前使用的屏幕驱动程序的变量。 GX_DRAW_CONTEXT 结构还包含用于绘画的画笔。 绘图上下文画笔是你将在自定义绘图函数中直接使用的工具。 画笔结构的定义如以下代码所示。

typedef struct GX_BRUSH_STRUCT
{
    GX_PIXELMAP *gx_brush_pixelmap;
    GX_FONT     *gx_brush_font;
    ULONG        gx_brush_line_pattern;
    ULONG        gx_brush_pattern_mask;
    GX_COLOR     gx_brush_fill_color;  
    GX_COLOR     gx_brush_line_color;  
    UINT         gx_brush_style;
    GX_VALUE     gx_brush_width;
    UCHAR        gx_brush_alpha;  
} GX_BRUSH;

Gx_brush_pixelmap 字段定义要用于矩形和多边形填充的像素图。 除非 gx_brush_style 函数包含 GX_BRUSH_PIXELMAP 样式,否则不会使用此工具。

gx_brush_font 函数可定义文本绘制使用的字体。 gx_brush_line_pattern 函数可定义虚线使用的模式。 gx_brush_style 函数是一组样式标志,可以组合使用以完整定义画笔特性。 可用画笔样式标志包括以下各项。

GX_BRUSH_OUTLINE
GX_BRUSH_SOLID_FILL
GX_BRUSH_PIXELMAP_FILL
GX_BRUSH_ALIAS
GX_BRUSH_UNDERLINE
GX_BRUSH_ROUND

gx_brush_width 函数可定义用于线条绘制的线条,或用于空心形状绘制的边框宽度。

gx_brush_line_color 函数可定义线条绘制和文本绘制的前景色。

gx_brush_fill_color 函数可定义用于填充形状的纯色填充色。 GUIX 上下文组件提供一组 API,旨在让你更轻松地在活动上下文中修改当前画笔。 这些 API 包括 gx_context_brush_define、gx_context_line_color_set、gx_context_fill_color_set、gx_context_font_set 等等。

父对象的绘图上下文由对象的子项继承。 实际上,当调用子对象的绘图函数时,其会继承父绘图上下文的复本。 子对象可以在不影响父绘图的情况下修改上下文,但也可以根据需要从父绘图(如画笔颜色和样式)继承信息。

GUIX 窗口组件

窗口组件负责 GUIX 中的所有窗口处理。 GUIX 窗口基本上是一个不同的显示区域,可能包含一个或多个子级小组件。 在 GUIX 中,该窗口实际上只是基本小组件对象的一种特殊形式。

GUIX 窗口以面向对象的方式实现,完全支持继承。 此操作通过使用 ANSI C 实现,这可以尽可能降低内存和处理要求。

GUIX 窗口主要通过添加对水平和垂直滚动的支持来扩展 GUIX 小组件的功能。 GUIX 窗口对象可以自动创建并显示滚动条,还能响应滚动条输入。 可移动窗口还具有内置事件处理,以便你根据笔输入事件移动或拖动窗口。 最后,GUIX 窗口通过将窗口移动到窗口 Z 顺序的前方来响应接收输入焦点。

GUIX 窗口一直秉持“客户端区域”的概念,即指从可用区域中删除窗口边框和非客户端对象(如滚动条)后窗口内的部分。 将客户端区域子级小组件裁剪为窗口工作区,非客户端子级(如滚动条)则可以在工作区之外绘制,但仍会被剪裁到窗口外部。

窗口以父子方式进行维护,其中子项从其父项继承特征。 子级窗口可能有其自己的子窗口,但也会从父级窗口继承各种特征。 任何窗口的特征都可以通过各种 GUIX API 调用进行显式重定义。

窗口创建

你可以在初始化期间或在执行应用程序线程期间随时创建窗口对象。 应用程序可以创建的窗口对象数没有限制。 任何窗口可能具有的子级数也没有限制。

窗口控制块

每个窗口对象的特征在位于其控制块 GX_WINDOW 中,并在 gx_api.h中定义。 窗口对象所需的内存由应用程序提供,并且可以位于内存中的任何位置。 不过,在任何函数的作用域外部定义窗口对象控制块最常见的方法是使其成为全局结构。

根窗口

GUIX 要求每张画布都要有一个根窗口。 根窗口无边框,与它所附加的画布具有相同的尺寸。 它主要用作所有第一级小组件和窗口的容器。 根窗口通常由应用程序在创建屏幕和画布之后,通过 API 函数 gx_window_root_create 创建而成。 如果使用 Studio 生成的函数 gx_studio_display_configure,则可以在传递给该函数的最后一个参数的位置中返回根窗口的地址。

根窗口默认不可移动,在最简单的情况下,根窗口大小即为画布大小。 根窗口实际可绘制显示背景,因此,若要更改显示背景色或显示背景墙纸,可以为根窗口指定颜色或墙纸。

如果根窗口可移动,则不会通过在画布上将其位置更改为子窗口来实现,而是移动画布本身。 此功能允许 GUIX 根窗口利用支持多帧缓冲区和硬件偏移注册的硬件。

背景

窗口背景为纯色或位图图像。 系统级别有默认的窗口背景,为初始窗口集提供默认值。 当然,你可以通过 GUIX API 更改任何窗口背景。

若要更改窗口的纯色背景,请使用 gx_widget_fill_color_set API。 若要将背景墙纸分配给窗口,请使用 gx_window_wallpaper_set API

滚动

当显示窗口子级所需的区域超出窗口的当前大小时(水平和/或垂直),GUIX 支持滚动标准窗口。 若要启用滚动,应用程序必须创建所需的滚动条,并将其附加到窗口中。

GUIX 窗口组件根据窗口工作区大小和所有子级小组件的范围提供了一个默认的滚动实现。 应用程序还可以通过覆盖特定窗口的 gx_window_scroll_info_get 函数来提供自己的滚动实现和解读

事件通知

默认窗口事件处理函数不同于 GX_WIDGET 事件处理,主要是处理滚动和调整大小事件。 GX_WINDOW 提供用于滚动和调整事件大小的默认处理程序。

应用程序还可以添加自己的自定义事件,并从常量 GX_FIRST_APP_EVENT 后以数字开始表示。 此常量后的所有事件号都保留给应用程序使用。 当然,应用程序的窗口事件处理程序必须要处理此类应用程序事件。

事件处理

与所有其他小组件类型一样,每个窗口都有一个默认的窗口事件处理函数,名为 gx_window_event_process。 你通常会在创建的窗口中使用自己的事件处理程序覆盖此事件处理函数。 这就是你将如何基于由窗口子控件生成的事件响应事件并采取措施的方式。

若要覆盖此事件处理程序,则一定要记得调用 GUIX 系统事件的基本 gx_window_event_process 函数,这样一来,你除了可以向事件处理程序添加任何操作以外,还可以执行默认事件处理。 例如,如果为 GX_EVENT_SHOW 事件提供自定义处理程序且未将此事件传递到 gx_window_event_process,则你的窗口将永远不可视。 若要为窗口提供自定义事件处理程序,请使用 gx_widget_event_process_set 函数来定义事件处理程序的地址。 此函数用 API 中指定的事件函数处理函数来覆盖默认事件处理函数。

重要

只需直接调用默认处理 gx_window_event_process,应用程序事件处理函数便可利用(即不重复处理)默认处理

只能从内部 GUIX 系统线程的上下文中调用事件处理。 如果采用这种方式,则处理事件处理的堆栈要求将仅适用于 GUIX 线程。

GUIX 图像读取器组件

图像读取器组件提供实用工具和 API 函数,以将原始压缩图像解压缩到 GUIX 像素图格式。 支持 JPEG 和 PNG 格式的原始图像数据,并为将来的版本保留其他格式。

请注意,绝大多数的 GUIX 应用程序都不需要 GUIX 图像读取器组件。 大多数应用程序依赖于 GUIX Studio 应用程序将 JPEG 和 PNG 格式的图形资产转换为兼容 GX_PIXELMAP 资源的 GUIX。 当原始图像资产仅在运行时已知时,或当系统存储约束阻止以 GX_PIXELMAP 格式存储资源时,系统将使用 GUIX 图像读取器组件。 JPEG 和 PNG 格式的图像数据通常比 GX_PIXELMAP 格式更为紧凑,但与执行这些图像类型的解压缩和颜色空间转换相关的运行时开销却很大。

如果将原始格式的 JPEG 或 PNG 图像传递到 gx_canvas_pixelmap_draw API 函数,GUIX 会动态解压缩并绘制 JPEG 或 PNG 数据。 请注意,这将对运行时绘制速度产生严重的负面影响,因此不建议你将原始格式的图像数据传递到 gx_canvas_pixelmap_draw 函数,除非你使用的硬件目标支持硬件辅助 JPEG 或 PNG 解压缩。

重要

将原始 JPEG 或 PNG 格式的图像传递到 gx_canvas_pixelmap_draw API 会对大多数目标硬件产生大量运行时开销。

作为替代方法,你可以在运行时使用图像读取器组件将原始 JPEG 和 PNG 数据转换为 GX_PIXELMAP 格式。 用这种方式生成的像素图可以像 Studio 生成的像素图一样被使用和绘制,并包含在你的资源文件中。 这可以让你的应用程序一次性执行图像解压缩、抖动和颜色空间转换(通常在程序启动期间),而不是每次绘制图像时都要执行这些操作。

图像读取器组件函数包括:

gx_image_reader_create
gx_image_reader_palette_set
gx_image_reader_start

GUIX 动画组件

GUIX 动画组件是一组用于自动执行屏幕和小组件转换自动化的函数和服务。 GUIX 动画组件支持任何小组件类型的淡入、淡出和移动或幻灯片类型动画。

。支持淡出类型动画的方式可以是通过改变淡出小部件内部 alpha 值(如果 GX_BRUSH_ALPHA_SUPPORT 已启用)或者通过将任何小组件集合绘制到单独的内存画布上,然后再与背景混合。 对于支持多个硬件图形层的硬件目标,使用此画布混合法可以最大程度实现平滑淡化效果,而且通常只需要很小的核心 CPU 带宽。 对于不支持多个图形层的硬件目标,在 16 bpp 和更高颜色深度下运行时,支持使用 GUIX 画笔 alpha 值混合。

如果动画应使用单独的绘图画布,GUIX 动画组件会提供 API 服务 gx_animation_canvas_define 以实现这一用途。 其他动画类型不需要单独的画布,但如果可用,也将使用。 这样一来,便可最大程度地使用多个硬件的基础硬件支持。

控制动画的变量由两个控制块定义。 首先,定义动画控制器的 GX_ANIMATION 控制块。 动画控制器是指执行你定义的动画序列的驱动引擎。 你可以多次重复使用单个动画控制器来运行多个不同的动画序列。 如果需要同时运行多个动画序列,你可以创建多个 GX_ANIMATION 动画控制器。

GUIX 系统组件可以提供 GX_ANIMATION 控制结构的重复使用块,而应用程序可以在需要动画时发出使用请求并在动画序列完成后自动返至系统池。 这将会针对每个可能实现的动画静态定义 GX_ANIMATION 结构,从而释放应用程序空间。 此 GX_ANIMATION 结构池的大小由常量 GX_ANIMATION_POOL_SIZE定义,默认值为 6,这意味着默认可以从系统池中分配 6 个同时性动画。 当然,如果需要更多同时性动画,你也可以在 gx_user.h 头文件中重新定义此常量。 如果将 GX_ANIMATION_POOL_SIZE 设为零,则 GUIX 系统组件将不提供动画池或相关服务。

用于定义动画的第二个控制块或结构是 GX_ANIMATION_INFO 结构。 此结构用于定义一个特定的动画序列。 将此信息结构传递到动画控制器,以使用 gx_animation_start API 服务启动动画序列。 GX_ANIMATION_INFO 结构包含以下字段:

typedef struct GX_ANIMATION_INFO_STRUCT
{
    GX_WIDGET *gx_animation_target;
    GX_WIDGET *gx_animation_parent;
    GX_WIDGET *gx_animation_screen_list;
    USHORT gx_animation_style;
    USHORT gx_animation_id;
    USHORT gx_animation_start_delay;
    USHORT gx_animation_frame_interval;
    GX_POINT gx_animation_start_position;
    GX_POINT gx_animation_end_position;
    GX_UBYTE gx_animation_start_alpha;
    GX_UBYTE gx_animation_end_alpha;
    GX_UBYTE gx_animation_steps;
} GX_ANIMATION_INFO;

gx_animation_target 函数可定义动画控制器将执行的目标小组件。

gx_animation_parent 函数可定义在动画序列完成时,目标小组件将附加的父级小组件对象(如有)。 gx_animation_parent 也是在动画完成时生成的 GX_ANIMATION_COMPLETE 事件的接收方。

gx_animation_screen_list 函数可定义适用于笔输入驱动的屏幕滑动动画的小组件列表。 应使用 GX_NULL 指针终止小组件列表,并且列表中的每个小组件与 gx_animation_parent 应具有相同的 x,y 尺寸。

gx_animation_style 是一个位掩码,用于定义要执行的动画类型和关联选项。 动画样式标志包括以下各项。

动画样式标志 说明
GX_ANIMATION_TRANSLATE 请求幻灯片或淡化类型动画。
GX_ANIMATION_SCREEN_DRAG 请求笔输入驱动的屏幕拖动动画。

以下标志可与 SCREEN_DRAG 类型动画结合使用。

屏幕拖动标志 说明
GX_ANIMATION_WRAP 屏幕列表应从结束转为开始。
GX_ANIMATION_HORIZONTAL 水平方向的屏幕拖动幅度。
GX_ANIMATION_VERTICAL 垂直方向的屏幕拖动幅度。

以下标志可与翻译动画结合使用。

翻译动画标志 说明
GX_ANIMATION_DETACH 在动画完成时,将动画目标与动画父对象拆离。 如果目标是通过 GUIX Studio 生成的自动事件处理进行动态分配和创建,则在拆离目标后,系统将删除该目标。
GX_ANIMATION_TRANSLATE 动画类型为计时器驱动动画。 应用程序可定义目标小组件的起始位置和结束位置以及起始和结束的 alpha 值,动画管理器则会创建一个计时器以作为执行动画的驱动力。
GX_ANIMATION_SCREEN_DRAG 此动画类型由笔输入事件驱动,因此不同于“转换”动画。 当用户在输入触摸屏上拖动笔或触控笔时,此动画类型会随着触摸屏输入跟踪目标部件。 若要使用此类型动画,应用程序应调用 gx_animation_drag_enable API 来启用此动画。

gx_animation_id 值将传递回 GX_ANIMATION_COMPLETE 事件的 event.gx_event_sender 字段的动画父级。 此值由动画父对象用于确定哪些子动画可能会报告完成情况。 此值可以为 0,并且 ID 值为 0 的动画根本不会生成 ANIMATION_COMPLETE 事件。

gx_animation_start_delay 值是一个 GUIX 滴答计数,指示在调用 gx_animation_start 之后、实际执行动画之前要延迟的计时器计时周期数。 该值可以为 0,以便在调用 gx_animation_start 时立即启动动画。

gx_animation_frame_interval 字段可定义在动画序列的每一帧之间延迟的 GUIX 计时器滴答数(基本 OS 滴答率的倍数)。 最小值为 1。

gx_animation_start_position 可定义翻译动画的目标小组件的左上起点。

gx_animation_end_position 可定义翻译类型动画目标动画目标小组件的左上角结束位置。

gx_animation_start_alpha 字段可定义翻译类型动画的起始画布 alpha 值。

gx_animation_end_alpha 字段可定义翻译类型动画的结束画布 alpha 值。

gx_animation_steps 字段可定义控制器应当为翻译动画执行的步骤数或帧数。 步骤越多,产生的滑动和/或淡出外观就越平滑,但也需要使用更大的系统带宽。

若要在应用程序中实现动画效果,必须先调用 gx_animation_create 以初始化动画控制器。 如果你的动画将使用辅助画布,请通过调用 gx_animation_canvas_define 初始化此画布。 接下来,你应该初始化 GX_ANIMATION_INFO 结构,以定义要执行的动画的特定类型以及其他动画参数。 最后,调用 gx_animation_start 触发动画序列。

当动画控制器完成动画序列时,其将向父级小组件发送 GX_ANIMATION_COMPLETE 事件,以允许你在该时间执行任何所需的动画画布清理操作。

GUIX 实用程序组件

实用工具组件负责 GUIX 中的所有常见实用工具函数。 这些常用函数都是非常有用的实用工具,可以从应用程序或内部 GUIX 代码的任何位置调用。 实用工具组件函数包括以下各项。

gx_utility_canvas_to_bmp

gx_utility_circle_point_get

gx_utility_alphamap_create

gx_utility_gradient_create

gx_utility_gradient_delete

gx_utlity_ltoa

gx_utility_math_acos

gx_utility_math_asin

gx_utility_math_cos

gx_utility_math_sin

gx_utility_math_sqrt

gx_utility_pixelmap_resize

gx_utility_pixelmap_rotate

gx_utility_pixelmap_simple_rotate

gx_utility_rectangle_center

gx_utility_rectangle_center_find

gx_utility_rectangle_combine

gx_utility_rectangle_compare

gx_utility_rectangle_define

gx_utility_rectangle_overlap_detect

gx_utility_rectangle_point_detect

gx_utility_rectangle_resize

gx_utility_rectangle_shift

gx_utility_string_to_alphamap