GUI 例程



Allegro 有一个原先基于 Atari GEM 系统的面向对象的对话管理程序 (form_do(),obj_draw(), 等等: 也许那些老ST程序员还知道我在说什么 :-) 用户可以使用 GUI 来制作一些简单的界面如测试程序和 grabber 工具, 或者将其当作用户制作自己更复杂的系统的基础.

Allegro 让你通过编写新的对话过程函数来定义自己的对象, 所以用户在通过 Allegro 控制鼠标, 键盘, 操作杆来处理输入时也可完全控制界面的视觉外观.


一个 GUI 对话是通过一个 DIALOG 对象数组来存储的, 其定义如下:

typedef struct DIALOG
{
   int (*proc)(int, DIALOG *, int); - 对话过程(消息句柄dialog procedure)
   int x, y, w, h;                  - 对象的位置和大小
   int fg, bg;                      - 前景和后景的颜色
   int key;                         - ASCII 键盘快捷码
   int flags;                       - 对象状态标记
   int d1, d2;                      - 用户自己任意设置该两项
   void *dp, *dp2, *dp3;            - 指向其他对象数据的指针
} DIALOG;

该数组应以一个设为 NULL 的 proc 指针的对象结束.

flags域可由以下位标记组成:

   D_EXIT          - 当该对象被点击时它应关闭对话
   D_SELECTED      - 该对象被选中
   D_GOTFOCUS      - 该对象拥有输入焦点
   D_GOTMOUSE      - 鼠标位于该对象之上
   D_HIDDEN        - 该对象不看见且为不活动的
   D_DISABLED      - 该对象呈灰色且为不活动的
   D_INTERNAL      - 该项为库专用,用户请不要使用
   D_USER          - 任何大于该项的2的幂次方都为用户保留,用户可任意使用

任何一个对象都是由保存在 proc 指针的对话过程函数所控制, 当该对象产生一个动作时,对话管理程序都将调用该过程函数. 用户也可以通过 SEND_MESSAGE 宏来直接调用. 对话过程函数形式如下:


   int foo(int msg, DIALOG *d, int c);

msg 参数表示对象将有什么样的动作, d 参数是一个指向该对象的指针, 如果 msg 为 MSG_CHAR 或 MSG_XCHAR 时 c 参数表示输入的字符. 注意, d 是一个指向指定对象而不是整个对话的指针.


对话过程函数返回下列值:

   D_O_K          - 标准返回值
   D_CLOSE        - 通知对话管理程序关闭对话
   D_REDRAW       - 通知对话管理程序重画整个对话
   D_WANTFOCUS    - 该对象请求输入焦点
   D_USED_CHAR    - 如果MSG_CHAR 和 MSG_XCHAR 输入了字符则返回该值

对话过程函数可被下列消息所调用:

MSG_START:
通知对象初始化自己. 对话管理程序在显示对话之前将该消息发送给对话中的所有对象.

MSG_END:
当对话结束时发送该消息给所有对象,通知其做清理工作.


MSG_DRAW:
通知对象在屏幕上重画自己.鼠标在该消息发送时被隐藏,所以重画代码不需关心它.



MSG_CLICK:
当鼠标在对象上且点击了一下时通知该对象. 一般说来一个对象将在鼠标键按下时开始跟 踪鼠轨迹直到鼠标键被释放,然后从该消息句柄返回.


MSG_DCLICK:
当用户双击一对象时产生该消息.当一个鼠标键被按下时产生 MSG_CLICK, 如果在一特定时间内鼠标键再此被按下则产生 MSG_DCLICK 消息.


MSG_KEY:
当对象的键盘快捷键被按下,或者在该对象拥有输入焦点时按下回车,空格, 或操作杆按钮时产生该消息.

MSG_CHAR:
当一个键被按下时,当前拥有输入焦点的对象获得该消息. 如果该对象处理击键动作则 MSG_CHAR 将返回D_USED_CHAR, 否则它将返回D_O_K来让省缺键盘接口来处理.


MSG_XCHAR:
当键盘上的一个键被按下时, Allegro将发送一个MSG_CHAR 消息给当前拥有输入焦点的对象.如果该对象不多该对象进行处理 (返回D_O_K 而不是 D_USED_CHAR), 对话管理程序将寻找一个有相同键盘快捷码, 然后向其发送MSG_KEY. 如果这也失败了, 对话管理程序将向对话中的所以对象发送 MSG_XCHAR 消息, 以使它们在没有输入焦点时也能够响应一些特殊按键. 一般情况下用户忽略该消息 (返回D_O_K而不是D_USED_CHAR), 这样 Allegro 将进行省缺处理如当按下箭头键时相应的改变输入焦点, 或在按下ESC键时关闭对话.


MSG_WANTFOCUS:
查询是否有对象愿意接收输入焦点. 如果该对象愿意接收输入焦点则返回 D_WANTFOCUS, 否则返回D_O_K.


MSG_GOTFOCUS:
MSG_LOSTFOCUS:
当一个对象得到或失去输入焦点时发送该消息. 该消息总是伴随者MSG_DRAW, 消息以便在该对象拥有输入焦点时可以被显示为不同的样子. 如果在处理 MSG_LOSTFOCUS 消息时返回 D_WANTFOCUS 将阻止对象因鼠标从自身移走而丢失焦点,所以该对象只会在其他对象准备 好接收焦点后才会丢失输入焦点 (d_edit_proc()对象就使用该技巧).



MSG_GOTMOUSE:
MSG_LOSTMOUSE:
当鼠标从一个对象移走或移动到一个对象时发送该消息. 与 MSG_GOTFOCUS 和 MSG_LOSTFOCUS 不同, 该消息之后不跟随 MSG_DRAW, 所以如果当鼠标在该对象之上时该对象是以不同方式显示, 则其收到该消息时必须负责重画自己.

MSG_IDLE:
当对话管理程序处于空闲状态时发送该消息.

MSG_RADIO:
当单选按钮按下时发送该消息. 单选组的编号通过 'c'参数传递.



MSG_USER:
用户可以任选一个大于 MSG_USER(MSG_USER+1, MSG_USER+2, ... MSG_USER+n) 的数做为自定义的消息值.


Allegro 通过几种标准对话过程函数. 用户可通过它们来实现一些简单的用户界面对象, 或者在用户自己的对话过程函数中调用, 就如OOP的继承性一样.举例来说,用户可构造一个调用d_button_proc 来绘制自己的对象,但通过不同的方法来处理点击的消息, 或者调用d_button_proc来做处理绘制自己以外的所有事情的对象, 所有该对象可以有象按钮一样的行为但看起来却完全不同.

int d_clear_proc(int msg, DIALOG *d, int c);
清除屏幕, 当其为对话中的第一个对象时有用.


int d_box_proc(int msg, DIALOG *d, int c);
int d_shadow_box_proc(int msg, DIALOG *d, int c);
在屏幕上绘制一个带或不带阴影的盒子.

int d_bitmap_proc(int msg, DIALOG *d, int c);
在屏幕上绘制一个由dp域所指的位图.


int d_text_proc(int msg, DIALOG *d, int c);
int d_ctext_proc(int msg, DIALOG *d, int c);
在屏幕上绘制由 dp 域所指向的一个字符串. d_ctext_proc() 使字符串在 X 坐标上居中. 任何一个'&'字符将被解释成在下一个字符下画上下划线. 要在屏幕上输出'&'字符则需输入'&&'字符. 使用其他的一些东东来替代缺省的 font 来画文字, 就需要设置 dp2 域来指向你设置的 font 数据.


int d_button_proc(int msg, DIALOG *d, int c);
一个按钮对象 (dp域指向一串文本字符串). 该对象可通过在其上点击鼠标键或键盘快捷键来选中. 如果设置了 D_EXIT 标记,则选中它将关闭该对话, 否则它将在开或关两种状态来回切换. 与d_text_proc()一样, '&'可以用来做按钮的快捷键.

int d_check_proc(int msg, DIALOG *d, int c);
这是一个向用户展示怎样从一个对象派生另一个对象的例子. d_button_proc()提供了大部份功能,但它自己却拥有一个复选框的外观.


int d_radio_proc(int msg, DIALOG *d, int c);
一个单选按钮.一个对话可以有任意数目的单选按钮组: 选取一个单选按钮将取消选定一个组中的其他单选按钮. dp域指向文本字符串,d1指明组的编号,d2指明按钮的风格(0=圆,1=矩形).



int d_icon_proc(int msg, DIALOG *d, int c);
一个位图按钮.fg颜色用来说明指明输入焦点的虚线颜色, bg用来指明当按钮被按下时用来填充上方和左方边界的阴影颜色. 的d1是"按下深度",即在没有"选中"图象时, 按钮选中后图标向下和右移动的象素个数. d2是用来显示交错状焦点的虚线的距离. dp是一个指向用来作图标的位图,dp2和dp3分别代表选中和非选中的图象 (可选,也可设为 NULL).



int d_keyboard_proc(int msg, DIALOG *d, int c);
这是一个用来作为键盘快捷键的不可见对象. 用户可在对话对象的键域中输入一个ASCII码 (一个字符如'a'来代表一次击键,或者一个1~26之间数来代表a~z键), 或者在 d1 和/或 d2 域中放入一个扫描码. 当这些键中的任何一个被按下后,该对象将调用有dp所指向的函数. 该函数将返回一个整型数,该整型数将被回专给对话管理程序, 这样该函数就可以返回D_O_K, D_REDRAW, D_CLOSE, 等等.


int d_edit_proc(int msg, DIALOG *d, int c);
一个可编辑的文本对象(dp域指向该字符串). 当它拥有输入焦点时(鼠标在其上点击),该对象可接收文本输入. d1 域指明它可接收的最大字符个数,d2指明文本光标在字符串中的位置.



int d_list_proc(int msg, DIALOG *d, int c);
一个列表框对象.该对象可让用户滚动一列项目, 并且提供鼠标或方向键来选择项目. 如果设置了D_EXIT标记则双击一个项目将关闭该对话. 选中项目的编号存放在d1域, d2 域指明该项目在列表框中滚动了多远.dp域是一个指向函数的指针, 该函数负责获取列表内容的信息,其形式如下:


      char *foobar(int index, int *list_size);

如果 index 是零或一个正数, 该函数将返回一个指向将在列表框相应位置显示的字符串的指针. 如果 index 是个负数, 该函数将返回NULL, list_size 将被设置成该列表框所含项目的个数. 如果要创建一个支持复选的列表框, 则需将dp2域设置成一个指明列表框中每一项的选择状态 (如果被选中则非零)的字节数组.该数组必须大于或等于该列表框中的项目个数!


int d_textbox_proc(int msg, DIALOG *d, int c);
一个文本框对象. dp域指向将在文本框中显示的文本. 如果文本过长, 则在文本框的右边会出现一个垂直滚动条,通过它用户可以滚动文本. 在省缺情况下文本框将按字缩进显示文本,但当设置了D_SELECTED标记后, 文本将按字符缩进格式显示.d1域用来指明文本的行数, d2用来指明文本滚动了多远.




int d_slider_proc(int msg, DIALOG *d, int c);
一个滑尺对象. d2 域指明滑动范围,在 0 到 d1 间取值. 如果 h 大于或等于 w 该对象将显示成一个垂直滑尺, 否则它将显示成一个水平滑尺. dp 域为可选,它指向一个用作滑尺把手的位图,dp2指向一个可选的回调函数, 该函数在d2值改变后被调用.该回调函数定义如下:


      int function(void *dp3, int d2);

d_slider_proc 对象返回该回调函数的返回值.

int d_menu_proc(int msg, DIALOG *d, int c);
该对象是一个菜单条对象. 当用户在其上点击或用快捷键选中某一菜单项时该对象将显示一下拉子菜单. 该对象忽略对话结构中的许多域, 特别是颜色是由gui_*_color变量来指定宽度和高度是自动确定. dp 域是一个指向存放菜单快捷方式的数组的指针:详阅do_menu().

顶层菜单将以水平菜单条的方式显示, 但当显示下拉子菜单时它将按do_menu()所使用的垂直方式显示. 当一个菜单条=被选中后,菜单回调函数的返回值将被传给对话管理程序,

该回调函数返回D_O_K, D_REDRAW, 或D_CLOSE.

对话管理程序的行为可由下列变量控制.

extern int gui_mouse_focus;
设置该变量后,输入焦点将所鼠标箭头的移动而移动, 否则需点击鼠标键来改变输入焦点.

extern int gui_fg_color, gui_bg_color;
标准对话的前景和后景色(警报,菜单,和文件选择器).省缺值为 255 和 0.


extern int gui_mg_color;
用来显示灰色化的对话对象(标注了D_DISABLE标记)的颜色.省缺值为8.


extern int gui_font_baseline;
如果设置成一个非零值,则将键盘快捷键的下划线, 按字体的下行字母的高度进行调整.

extern int (*gui_mouse_x)();
extern int (*gui_mouse_y)();
extern int (*gui_mouse_b)();
沟通函数 , 无论何时当 GUI 例程需要控制鼠标状态时都会调用. 缺省这些返回 mouse_x, mouse_y, 和 mouse_b 变量的拷贝, 但是他们用来偏移或量取鼠标坐标, 或者从完全不同的源来读输入.

用户可由提供改变全局的'font'指针来使GUI对象使用标注8x8字体以外的字体. 标准对话过程程序和警告框可以使用任何大小的字体, 但 file_select() 和 gfx_mode_select() 对话只能使用8x8的字体.



int gui_textout(BITMAP *bmp, unsigned char *s, int x, y, color, centre);
GUI 程序所使用的帮助函数.该函数将在屏幕上显示文本字符串, 并将'&'解释成键盘快捷键的下划线.该函数返回输出字符串的象素宽度.


int gui_strlen(unsigned char *s);
帮助函数. 返回一个字符串的象素宽度.忽略'&'字符.


void centre_dialog(DIALOG *dialog);
移动一组对话对象,使其在屏幕上居中.

void set_dialog_color(DIALOG *dialog, int fg, int bg);
为一组对话对象设置前景和后景色.

int find_dialog_focus(DIALOG *dialog);
在对话中寻找拥有输入焦点的对话对象,如果找到则返回该对象的索引值, 否则返回-1.该函数对那些多次调用 do_dialog() 并且想保持输入焦点不变的用户十分有用.比如用户可这样调用该函数 do_dialog(dlg, find_dialog_focus(dlg));


int dialog_message(DIALOG *dialog, int msg, int c, int *obj);
项一组对象发送消息.如果任何一个对话过程函数返回一个D_O_K以外的值, 它将返回该值并且将obj设置成该对象的索引.


int broadcast_dialog_message(int msg, int c);
向一个活动对话中的所有对象发送一条消息. 如果任何一个对话过程函数返回非D_O_K值,则它返回该值.


int do_dialog(DIALOG *dialog, int focus_obj);
基本对话管理程序函数.该函数将显示一个对话(一组对话对象, 有其中包含NULL的对话过程函数结),并且将输入焦点设置给focus_obj (如果用户不想让任何对象拥有输入焦点则向focus_obj传递-1). 该函数解释用户的输入并向对象发送消息, 直到其中的一个对话过程函数要求关闭对话,这时它将返回关闭对话的对象的索引值.



int popup_dialog(DIALOG *dialog, int focus_obj);
功能与do_dialog()基本相同,不同之处在于它将保存屏幕上的数据, 并且在对话关闭后将其恢复. 被恢复的屏幕区域尺寸是根据对话中的第一个对象的大小来决定, 所以其他对象应当位于该对象之中.

DIALOG_PLAYER *init_dialog(DIALOG *dialog, int focus_obj);
该函是一个底层函数.它提供与do_dialog()象素的功能, 并且容许用户将一个对话框与自己定义的程序控制结构关联在一起. 该函数初始化一个对话,返回一个指向可被update_dialog 和shutdown_dialog()使用的对象. 有了这些函数用户可编写自己的do_dialog()函数.如下所示:


      void *player = init_dialog(dialog, focus_obj);

while (update_dialog(player)) ;

return shutdown_dialog(player);

int update_dialog(DIALOG_PLAYER *player);
更新由init_dialog()函数返回的对象的状态. 如果该对话是活动的则返回TRUE,如果被关闭则返回FALSE, 是调用shutdown_dialog()还是继续执行由用户决定. 可从player->obj 域判断哪个对象请求关闭对话.


int shutdown_dialog(DIALOG_PLAYER *player);
撤销一个由init_dialog()函数返回的使用者对象(player object), 并且返回关闭对话的对象(与do_dialog()的返回值相同).


extern DIALOG *active_dialog;
全局指针,指向最近活动对话.这对一个要扫描其所有同胞对象的对象十分有用.



弹出式和下拉式菜单是有一组结构来定义的.

typedef struct MENU
{
   char *text;                   - 菜单桑显示的条目
   int (*proc)();                - 当菜单项被点后调用的函数
   struct MENU *child;           - 子菜单组
   int flags;                    - 关闭或选择的状态
   void *dp;                     - 指向你需要的任何数据
} MENU;

任何一个菜单项都有一个文本字符串, 用户可用'&'字符来定义一个快捷键或一个空字符串来表示一个不可选的分割条. 如果该字符串包含一个"\t"制表符,则其后的文本将被右对齐以便用来显示快捷键信息. proc是一个函数指针, 该函数在菜单项选中后被调用. child参数指向另一个菜单, 用户可通过它创建内嵌式菜单. proc和child都可以为NULL. proc 指向的函数返回一个整数值, 如果菜单是通过do_menu()来创建的则该返回值被忽略, 如果菜单是通过d_menu_proc()对象创建的则该返回值被传给对话管理程序. 该组菜单项将被一个包含 NULL 文本指针的项目关闭. 菜单项可通过在flags域设置 D_DISABLED 标记来被禁止(灰色显示). 通过设置D_SELECTED标记可在菜单项旁边显示一个复选标记. 在省缺对齐方式和字体下这将使复选标记和菜单文本重叠, 所以在使用带复选标记的菜单项时, 最好在菜单文本前输入两个空格符以便正确显示复选标记.


int do_menu(MENU *menu, int x, int y)
在指定屏幕坐标显示并激活一个弹出菜单 (如果菜单大小不能完全适合屏幕,该函数将会对其进行调整). 返回选中菜单项的索引,或者返回-1如果该菜单项被取消. 注意该函数不能返回子菜单项是否被选中.所以, 如果用户想使用多级菜单则必须自己调用回调函数.


extern MENU *active_menu;
当一个菜单回调函数被调用时,它将被设置给选中的菜单项, 如此用户的程序就可以知道它是在何处被调用.


int alert(char *s1, *s2, *s3, char *b1, *b2, int c1, c2);
显示一个拥有3个文本行(s1-s3),和一到两个按钮的弹出式报警框. 按钮上的文本是通过b1和b2参数来传递的(b2可为 NULL), 快捷键是通过c1和c2来定义的.返回值 1 或 2 与按下那个按钮有关. 如果通过按下ESC键来解除报警(这时ESC不是快捷键), 与按下第二个按钮效果一样(这与"OK","Cancel"报警框相同).



int alert3(char *s1, *s2, *s3, char *b1, *b2, *b3, int c1, c2, c3);
与alert()相似,但是有三个按钮返回1,2,3三个值.

int file_select(char *message, char *path, char *ext);
显示Allegro文件选择器,并用message所指向的字符串作为标题栏. path参数是初始显示的文件名(它可以被用来作起始目录, 或为save-as操作提供省缺文件名).用户选择的目录长短不一, 所以至少要有80个字符的空间. 放在文件选择器中的文件是根据ext参数所指定的文件扩展名决定的, 将 ext 参数设置成NULL则表明在文件选择器中放入所有文件. 如 "PCX;BMP" 将只装入 .PCX 和 .BMP文件. 如果用户选择取消键该函数返回零值,如果选择OK键该函数返回一个非零值.


int gfx_mode_select(int *card, int *w, int *h);
显示Allegro图象模式选择对话,通过它用户可以选择屏幕模式和显示卡. 在3个参数中存放用户的选择.如果用户选择取消键该函数返回零值, 如果选择OK键该函数返回非零值.


int gfx_mode_select_ex(int *card, int *w, int *h, int *color_depth);
图象模式选择对话的扩充版本. 它准许用户在选择分辨率和硬件驱动程序的同时选择颜色深度. 该函数在对话被激活时将从参数中读取初始值,所以用户可通过参数来设置省缺值.





返回