显示数值控件
LaGUI 的所有数值控件均需要数据源才能显示。因此我们需要为您应用程序的业务数据注册数据的访问方式,这个“访问方式”在 LaGUI 中定义成一条条的属性。
通过您定义的属性,LaGUI 还自动管理数据的撤销和重做,也包括资源文件的读写。交由 LaGUI 撤销系统、修改记录系统和共享资源系统来管理的数据必须使用 LaGUI 的内存调用来分配。在接下来的例子中,我们只需要用到界面显示,因此涉及不到这些复杂用法。
最简单的属性定义例子
例如我们有这样一个全局的 C 定义:
typedef struct My{
    int           _pad;
    laSafeString* Name;
    int           Age;
    int           Gender;
    real          Height;
} My;
My Stats;
在 LaGUI 中,C Struct 相当于 laPropContainer 。我们首先创建一个适用于 My 类型的 laPropContainer :
laPropContainer* my=laAddPropertyContainer("my", "My", "Struct My",0,0,0,0,0,LA_PROP_OTHER_ALLOC);
由于所有 My 实例(在这里只有一个 My Stats;)的内存都不由 LaGUI 分配,因此在最后一个参数必须设置 LA_PROP_OTHER_ALLOC 以告知 LaGUI ,同时由于在这个例子中我们不需要 LaGUI 创建或者删除 My 实例,也不需要赋值 NodeSize 参数。
接下来我们就可以向 my 这个 laPropContainer 中添加各个属性,使用对应的 laAddxxxxProperty() 函数。这个例子足够简单,我们不需要 get/set 回调,因此只需提供成员相对于 My 的首地址偏移量。
laAddStringProperty(my, "name", "Name", "My name",0,0,0,0,1,offsetof(My,Name),0,0,0,0,0);
laAddIntProperty(my, "age", "Age", "My age",0,0,"years old",100,0,1,25,0,offsetof(My,Age),0,0,0,0,0,0,0,0,0,0,0);
laAddFloatProperty(my, "height", "Height", "My height",0,0,"cm",2,0.3,0.01,1.76,0,offsetof(My,Height),0,0,0,0,0,0,0,0,0,0,0);
laProp* ep=laAddEnumProperty(my, "gender","Gender","My gender",0,0,0,0,0,offsetof(My,Gender),0,0,0,0,0,0,0,0,0,0);
laAddEnumItemAs(ep,"MALE","Male","Gender being male",0,L'♂');
laAddEnumItemAs(ep,"FEMALE","female","Gender being female",1,L'♀');
注意到 laSafeString* Name; 不是 char[] ,LaGUI 提供了 laSafeString 的便利功能,只需在注册属性时将 IsSafeString 参数置为非0。
此外,我们要告诉 LaGUI 我们业务数据的根,这样 LaGUI 才能找到第一个 my 的实例(在这个例子中只有一个)。
laPropContainer* root=laDefineRoot();
laAddSubGroup(root,"me","Me","Me root", "my", 0,0,0,0,myget_Stats,0,0,0,0,0,0,0);
属性到这里就注册完成了,现在可以在界面上显示刚才注册的这些属性:
void MyProperties(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
    laColumn* c=laFirstColumn(uil);
    laShowLabel(uil,c,"Hello world!",0,0);
    laShowItem(uil,c,0,"me.name");
    laShowItem(uil,c,0,"me.age");
    laShowItem(uil,c,0,"me.height");
    laShowItem(uil,c,0,"me.gender");
}
属性注册与面板注册的先后顺序无所谓。之后,对 My Stats; 的值初始化之后,就能够运行程序了。你可以通过控件修改这些属性的值,如果通过“🞆”菜单调出一个新的“Properties”面板,你可以观察到两个面板上的属性同步刷新。
属性简易示例程序的代码应该类似于这样:
#include "la_5.h"
extern LA MAIN;
typedef struct My{
    int           _pad;
    laSafeString* Name;
    int           Age;
    int           Gender;
    real          Height;
} My;
My Stats;
void* myget_Stats(void* unused, void* unused1){
    return &Stats;
}
void MyProperties(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
    laColumn* c=laFirstColumn(uil);
    laShowLabel(uil,c,"Hello world!",0,0);
    laShowItem(uil,c,0,"me.name");
    laShowItem(uil,c,0,"me.age");
    laShowItem(uil,c,0,"me.height");
    laShowItem(uil,c,0,"me.gender");
}
int main(int argc, char *argv[]){
    laGetReady();
    Stats.Age=25;
    Stats.Gender=0;
    Stats.Height=1.76;
    strSafeSet(&Stats.Name,"ChengduLittleA");
    laPropContainer* root=laDefineRoot();
    laAddSubGroup(root,"me","Me","Me root", "my", 0,0,0,0,myget_Stats,0,0,0,0,0,0,0);
    laPropContainer* my=laAddPropertyContainer("my", "My", "Struct My",0,0,0,0,0,LA_PROP_OTHER_ALLOC);
    laAddStringProperty(my, "name", "Name", "My name",0,0,0,0,1,offsetof(My,Name),0,0,0,0,0);
    laAddIntProperty(my, "age", "Age", "My age",0,0,"years old",100,0,1,25,0,offsetof(My,Age),0,0,0,0,0,0,0,0,0,0,0);
    laAddFloatProperty(my, "height", "Height", "My height",0,0,"cm",2,0.3,0.01,1.76,0,offsetof(My,Height),0,0,0,0,0,0,0,0,0,0,0);
    laProp* ep=laAddEnumProperty(my, "gender","Gender","My gender",0,0,0,0,0,offsetof(My,Gender),0,0,0,0,0,0,0,0,0,0);
    laAddEnumItemAs(ep,"MALE","Male","Gender being male",0,L'♂');
    laAddEnumItemAs(ep,"FEMALE","female","Gender being female",1,L'♀');
    laRegisterUiTemplate("my_properties","Properties", MyProperties,0,0,"Demonstration", 0,0,0);
    // Uncomment this to load preferences.
    // laEnsureUserPreferences();
    if(!MAIN.Windows.pFirst){
        laWindow* w = laDesignWindow(-1,-1,600,600);
        laLayout* l = laDesignLayout(w,"My Layout");
        laCreatePanel(l->FirstBlock,"my_properties");
        laStartWindow(w);
    }
    laMainLoop();
}
属性定义参考
LaGUI 支持的属性类型如下表所示:
属性类型
对应 C 类型
LaGUI 控制的操作
LA_PROP_INT
32位整数
读、写、数组、显示
LA_PROP_FLOAT
64位浮点数
读、写、数组、显示
LA_PROP_ENUM
8/16/32位整数
读、写、数组、显示
LA_PROP_STRING
8位整数数组或 laSafeString*
读、写、显示
LA_PROP_SUB
64位地址或 laListHandle
写指针、读指针和偏移、读写列表、显示列表和成员
LA_PROP_RAW
64位地址
(仅通过回调在文件读写时访问)
LA_PROP_OPERATOR
-
通过 This 的工具调用1
1: 目前属性路径必须仅包含工具属性标识符,否则不工作。
LA_PROP_SUB 属性可递归包含,因此可以以树状方式描述整个应用程序的数据结构。下面这个对照示意解释了一种简易文件树结构的可能注册方式。建议通过各个 LaGUI 示例程序以及“Our Paint”软件的源代码更详细地了解向 LaGUI 描述您业务数据结构的方法。
        数据结构                 |         属性注册样式
                                |
struct Folder{                  |
    laListItem Item;            |    SUB "folder" 
    char Name[128];             |        STRING "name" 
    int Privileges;             |        INT "privileges"
    laListHandle SubFolders;    |        SUB_PROP LIST "folders" of "folder"
    laListHandle Files;         |        SUB_PROP LIST "files" of "file"
};                              |    
                                |    
struct File{                    |    
    laListItem Item;            |    SUB "file" 
    char Name[128];             |        STRING "name"
    int Size;                   |        INT "size"
    void* Data;                 |        RAW "data"
};                              |
                                |
struct FileBrowser{             |    SUB "application"
    int SomeOtherStuff;         |         SUB_PROP LIST "folders" of "folder"
    laListHandle SubFolders;    |
};                              |
                                |    SUB "(__LAGUI_ROOT__)" 
                                |        SUB_PROP "my_application" of "application"