Written by Mariano Alvarez Fernández on October 1, 2019
Last update: October 1, 2019
GrGUI is a mini graphics user interface running on top of MGRX. It wants to be small, easy to use and not intrusive whit your program design, this is why it doesn't have a main loop to take control, instead you send individual events to specific functions when you want the GUI takes care of them. You don't need to use all the GUI functionality, you can (by example) use only the menues or the menu bar or some dialogs or some buttons or the GUI Contexts, it's up to you.
In this document, instead to present a detailed manual of each function, we will present eleven examples that covers the GrGUI basic concepts.
Because all MGRX functions begin with "Gr" and all GrGUI functions begin with "GUI" yo can easily know what code is GrGUI related or standard MGRX code.
You can find all the examples in the MGRX distribution under the "testgui" subdirectory.
#include <stdlib.h> #include <stdio.h> #include <grgui.h> int main() { char *abouttext[4] = { "Welcome to MGRX and GrGUI", "MGRX is a small C 2D graphics library", "and GrGUI a miniGUI on top of MGRX", "visit mgrx.fgrim.com for more info"}; GrSetMode(GR_default_graphics); GUIInit(1, 0); GUICDialogInfo("Hello GrGUI", (void **)abouttext, 4, "Ok"); GUIEnd(); GrSetMode(GR_default_text); return 0; }
Note we only include "grgui.h", because internally it includes "mgrx.h" and even "mgrxkeys.h".
"GUIInit" expect the graphics mode to be set before calling it. GUInit takes two
parameters that can be 1 (true) or 0 (false). The first one indicates if it must
init the MGRX input subsystem. The second if it must use a double-buffer for the
graphics output. Using a double-buffer we get a smoother output and it can be
faster for most videodrivers, but it needs the user to indicate when bitblt to
the screen if the graphic output is not done by GrGUI functions. We will see it
in the last example.
#include <stdlib.h> #include <stdio.h> #include <grgui.h> int main() { GUIContext *gctx1, *gctx2, *gctx3; GrEvent ev; GrSetMode(GR_default_graphics); GUIInit(1, 0); GrClearScreen(GrAllocColor(0, 100, 0)); gctx1 = GUIContextCreate(100, 100, 200, 200, 1); gctx2 = GUIContextCreate(150, 150, 250, 250, 1); gctx3 = GUIContextCreate(200, 50, 400, 300, 1); if (gctx1 == NULL || gctx2 == NULL || gctx3 == NULL) exit(1); GrTextXY(10, 10, "Test GUIContexts, press any key to continue", GrWhite(), GrNOCOLOR); GUIContextSaveUnder(gctx1); GrSetContext(gctx1->c); GrClearContext(GrAllocColor(100, 0, 0)); GrEventWaitKeyOrClick(&ev); GUIContextSaveUnder(gctx2); GrSetContext(gctx2->c); GrClearContext(GrAllocColor(0, 0, 100)); GrEventWaitKeyOrClick(&ev); GUIContextSaveUnder(gctx3); GrSetContext(gctx3->c); GrClearContext(GrAllocColor(200, 200, 0)); GrEventWaitKeyOrClick(&ev); GUIContextRestoreUnder(gctx3); GUIContextDestroy(gctx3); GrEventWaitKeyOrClick(&ev); GUIContextRestoreUnder(gctx2); GUIContextDestroy(gctx2); GrEventWaitKeyOrClick(&ev); GUIContextRestoreUnder(gctx1); GUIContextDestroy(gctx1); GrEventWaitKeyOrClick(&ev); GUIEnd(); GrSetMode(GR_default_text); return 0; }
GUI Contexts are the basic GrGUI container, they provides a screen (or memory
if we asked for double-buffer) subcontext to draw in. They can save the actual
graphic contents under the context and restore it later.
#include <stdlib.h> #include <stdio.h> #include <grgui.h> #define COMMAND_OPTION1 1 #define COMMAND_OPTION2 2 #define COMMAND_OPTION3 3 #define COMMAND_OPTION4 4 #define COMMAND_EXIT 5 #define ID_MENU1 1 #define ID_MENU2 2 void print_line(char *s) { #define LINE_HIGH 16 static int ypos = 10; if (ypos >= GrMaxY() - LINE_HIGH) { GrClearContext(GrBlack()); ypos = 10; } GrTextXY(10, ypos, s, GrWhite(), GrBlack()); ypos += LINE_HIGH; } int main() { static GUIMenuItem itemsm1[6] = { {GUI_MI_OPER, 1, "Option &1", '1', NULL, 0, COMMAND_OPTION1, 0}, {GUI_MI_OPER, 1, "Option &2", '2', NULL, 0, COMMAND_OPTION2, 0}, {GUI_MI_SEP, 1, "", 0, NULL, 0, 0, 0}, {GUI_MI_MENU, 1, "&Submenu", 'S', NULL, 0, ID_MENU2, 0}, {GUI_MI_SEP, 1, "", 0, NULL, 0, 0, 0}, {GUI_MI_OPER, 1, "E&xit", 'X', NULL, 0, COMMAND_EXIT, 0}}; static GUIMenu menu1 = {ID_MENU1, 6, 0, itemsm1}; static GUIMenuItem itemsm2[2] = { {GUI_MI_OPER, 1, "Option &3", '3', NULL, 0, COMMAND_OPTION3, 0}, {GUI_MI_OPER, 1, "Option &4", '4', NULL, 0, COMMAND_OPTION4, 0}}; static GUIMenu menu2 = {ID_MENU2, 2, 0, itemsm2}; GrEvent ev; int result; char s[81]; GrSetMode(GR_default_graphics); GUIInit(1, 0); GUIMenuRegister(&menu1); GUIMenuRegister(&menu2); print_line("Press R to run menu, Esc to quit"); while(1) { GrEventWait(&ev); if (ev.type == GREV_KEY) { if (ev.p1 == GrKey_Escape) break; if (ev.p1 == 'r' || ev.p1 == 'R') { result = GUIMenuRun(ID_MENU1, 100, 100, 0); sprintf(s,"GUIMenuRun returned %d", result); print_line(s); } } if (ev.type == GREV_COMMAND) { if (ev.p1 == COMMAND_EXIT) { print_line("Received COMMAND_EXIT event, exiting in 3 seconds"); GrMouseEraseCursor(); GrSleep(3000); break; } sprintf(s,"Received COMMAND event %ld", ev.p1); print_line(s); } } GUIEnd(); GrSetMode(GR_default_text); return 0; }
We define menues statically as a list of menu items, they can be Operations, Separator or other Menues. When user selects an Operation a GREV_COMMAND event is generated. You can cancel a menu pressing the Escape key or clicking the mouse outside the menu area.
Menues must be registered with the "GUIMenuRegister" funtion, so that a menu can call another menu.
Obviusly a menu uses a GUI Context.
#include <stdlib.h> #include <stdio.h> #include <grgui.h> #define COMMAND_OPTION1 1 #define COMMAND_OPTION2 2 #define COMMAND_OPTION3 3 #define COMMAND_OPTION4 4 #define COMMAND_OPTION5 5 #define COMMAND_OPTION6 6 #define COMMAND_EXIT 7 #define ID_MENU1 1 #define ID_MENU2 2 #define ID_MENU3 3 void print_line(char *s) { #define LINE_HIGH 16 static int ypos = 10; if (ypos >= GrMaxY() - LINE_HIGH) { GrClearContext(GrBlack()); ypos = 10; } GrTextXY(10, ypos, s, GrWhite(), GrBlack()); ypos += LINE_HIGH; } int main() { static GUIMenuItem itemsm1[6] = { {GUI_MI_OPER, 1, "Option &1", '1', NULL, 0, COMMAND_OPTION1, 0}, {GUI_MI_OPER, 1, "Option &2", '2', NULL, 0, COMMAND_OPTION2, 0}, {GUI_MI_SEP, 1, "", 0, NULL, 0, 0, 0}, {GUI_MI_MENU, 1, "&Submenu", 'S', NULL, 0, ID_MENU2, 0}, {GUI_MI_SEP, 1, "", 0, NULL, 0, 0, 0}, {GUI_MI_OPER, 1, "E&xit", 'X', "Ctrl+X", GrKey_Control_X, COMMAND_EXIT, 0}}; static GUIMenu menu1 = {ID_MENU1, 6, 0, itemsm1}; static GUIMenuItem itemsm2[2] = { {GUI_MI_OPER, 1, "Option &3", '3', NULL, 0, COMMAND_OPTION3, 0}, {GUI_MI_OPER, 1, "Option &4", '4', NULL, 0, COMMAND_OPTION4, 0}}; static GUIMenu menu2 = {ID_MENU2, 2, 0, itemsm2}; static GUIMenuItem itemsm3[4] = { {GUI_MI_OPER, 1, "Option &5", '5', NULL, 0, COMMAND_OPTION5, 0}, {GUI_MI_OPER, 1, "Option &6", '6', NULL, 0, COMMAND_OPTION6, 0}, {GUI_MI_SEP, 1, "", 0, NULL, 0, 0, 0}, {GUI_MI_MENU, 1, "&Submenu", 'S', NULL, 0, ID_MENU2, 0}}; static GUIMenu menu3 = {ID_MENU3, 4, 0, itemsm3}; static GUIMenuBarItem mbitems[2] = { {"&First_menu", 1, GrKey_Alt_F, ID_MENU1}, {"&Second_menu", 1, GrKey_Alt_S, ID_MENU3}}; static GUIMenuBar menubar = {2 ,0, mbitems}; GrEvent ev; char s[81]; GrContext *ctx; GrSetMode(GR_default_graphics); GUIInit(1, 0); GUIMenuRegister(&menu1); GUIMenuRegister(&menu2); GUIMenuRegister(&menu3); GUIMenuBarSet(&menubar); GUIMenuBarShow(); ctx = GrCreateSubContext(0, GUIMenuBarGetHeight(), GrMaxX(), GrMaxY(), NULL, NULL); GrSetContext(ctx); print_line("Try the MenuBar above"); while(1) { GrEventWait(&ev); if (ev.type == GREV_KEY) { if (ev.p1 == GrKey_Escape) break; } if (ev.type == GREV_COMMAND) { if (ev.p1 == COMMAND_EXIT) { print_line("Received COMMAND_EXIT event, exiting in 3 seconds"); GrMouseEraseCursor(); GrSleep(3000); break; } sprintf(s,"Received COMMAND event %ld", ev.p1); print_line(s); } } GUIEnd(); GrSetMode(GR_default_text); return 0; }
The menu bar is statically defined as a list of menu bar items. Each item points to a menu id. You can have only a menu bar at any time that is registered using the "GUIMenuBarSet" function. After that you can show it with the "GUIMenuBarShow" function. When showed you can select a menu clicking with the mouse or using the ALT+letter combination defined. When tied to the menu bar, menu operations can define keyboard shortcuts to acces the operation directly, in our example "Ctrl-X" executes the Exit operation.
The funtion "GUIMenuBarGetHeight" is important because it reports the menu
bar height in pixels, so we can set a screen subcontext as our drawing area
without interfere with the menu bar.
#include <stdlib.h> #include <stdio.h> #include <grgui.h> typedef struct { GUIPanel *gp; GrColor fg; GrColor bg; } UserData; void paint_panel1(void *data) { UserData *ud; ud = (UserData *)data; GrClearContext(ud->bg); GrTextXY(10, 10, "This is a simple panel with 1 px border", ud->fg, ud->bg); GrTextXY(10, 26, "Press R to reverse colors, C to continue", ud->fg, ud->bg); } void paint_panel2(void *data) { UserData *ud; ud = (UserData *)data; GrClearContext(ud->bg); GrTextXY(10, 10, "This is a panel with 4 px border,", ud->fg, ud->bg); GrTextXY(10, 26, "title and scroll bars", ud->fg, ud->bg); GrTextXY(10, 42, "Press R to reverse colors, C to continue", ud->fg, ud->bg); } void paint_panel2_title(void *data) { UserData *ud; ud = (UserData *)data; GrClearContext(ud->fg); GrTextXY(2, 2, "This is the panel 2 title", ud->bg, ud->fg); GrHLine(0, GrMaxX(), GrMaxY(), ud->bg); } int process_panel_event(void *data, GrEvent *ev) { UserData *ud; GrColor aux; ud = (UserData *)data; if (ev->type == GREV_KEY) { if (ev->p1 == 'r' || ev->p1 == 'R') { aux = ud->fg; ud->fg = ud->bg; ud->bg = aux; ud->gp->paintcl(data); return 1; } if (ev->p1 == 'c' || ev->p1 == 'C') { return -1; } } return 0; } int main() { GUIPanel *gp1, *gp2; GrEvent ev; UserData ud; int ret; GrSetMode(GR_default_graphics); GUIInit(1, 0); GrClearScreen(GrAllocColor(0, 100, 0)); gp1 = GUIPanelCreate(100, 100, GrMaxX()-200, GrSizeY()-200, GUI_PCAPB_SU, 1, 0); gp2 = GUIPanelCreate(100, 100, GrMaxX()-200, GrSizeY()-200, GUI_PCAPB_SU|GUI_PCAPB_VSCB|GUI_PCAPB_HSCB, 4, 20); if (gp1 == NULL || gp2 == NULL) exit(1); ud.gp = gp1; ud.fg = GrBlack(); ud.bg = GrWhite(); GUIPanelSetClCallBacks(gp1, paint_panel1, process_panel_event); GUIPanelSetUserData(gp1, (void *)&ud); GUIPanelPaint(gp1, GrBlack(), GrWhite()); while(1) { GrEventWait(&ev); ret = GUIPanelProcessEvent(gp1, &ev); if (ret == -1) break; } GUIContextRestoreUnder(gp1->gc); GUIPanelDestroy(gp1); ud.gp = gp2; ud.fg = GrAllocColor2(0x555555); ud.bg = GrAllocColor2(0x55FFFF); GUIPanelSetClCallBacks(gp2, paint_panel2, process_panel_event); GUIPanelSetTlCallBack(gp2, paint_panel2_title); GUIPanelSetUserData(gp2, (void *)&ud); GUIPanelPaint(gp2, GrBlack(), GrWhite()); while(1) { GrEventWait(&ev); ret = GUIPanelProcessEvent(gp2, &ev); if (ret == -1) break; } GUIContextRestoreUnder(gp2->gc); GUIPanelDestroy(gp2); GUIEnd(); GrSetMode(GR_default_text); return 0; }
A GUI Panel is a more elaborated container on top of a GUI Context, it can
have a border, a title area, a client area and Scrollbars. And more important
you can attach a client area paint, a title area paint and a event processing
functions to them.
#include <stdlib.h> #include <stdio.h> #include <grgui.h> void print_line(char *s) { #define LINE_HIGH 16 static int ypos = 10; if (ypos >= GrMaxY() - LINE_HIGH) { GrClearContext(GrBlack()); ypos = 10; } GrTextXY(10, ypos, s, GrWhite(), GrBlack()); ypos += LINE_HIGH; } int main() { char *bodytext[2] = { "This is a GrGUI common dialog", "select one option"}; GrEvent ev; char s[81]; int result; GrSetMode(GR_default_graphics); GUIInit(1, 0); print_line("Press 1 to run dialog Yes/No"); print_line("Press 2 to run dialog Yes/No/Cancel"); print_line("Esc to quit"); while(1) { GrEventWait(&ev); if (ev.type == GREV_KEY) { if (ev.p1 == GrKey_Escape) break; if (ev.p1 == '1') { result = GUICDialogYesNo("Test Yes/No", (void **)bodytext, 2, "Yes", "No"); sprintf(s,"Dialog Yes/No returned %d", result); print_line(s); } if (ev.p1 == '2') { result = GUICDialogYesNoCancel("Test Yes/No/Cancel", (void **)bodytext, 2, "Yes", "No", "Cancel"); sprintf(s,"Dialog Yes/No returned %d", result); print_line(s); } } } GUIEnd(); GrSetMode(GR_default_text); return 0; }
A GUI Dialog is a another container on top of a GUI Panel. GrGUI provides some common dialogs, one of them was used in the first example to show some information to the user. The other two, to ask a Yes/No question or a Yes/No/Cancel question to the user, are showed in this example.
In example 10 we will see how to construct our own dialog.
#include <stdlib.h> #include <stdio.h> #include <grgui.h> typedef struct { GUITile *gt; GrColor fg; GrColor bg; } UserData; void paint_tile1(void *data) { UserData *ud; ud = (UserData *)data; GrClearContext(ud->bg); GrTextXY(10, 16, "This is a passive tile with 1 px border", ud->fg, ud->bg); } void paint_tile2(void *data) { UserData *ud; ud = (UserData *)data; GrClearContext(ud->bg); GrTextXY(10, 10, "This is an active tile", ud->fg, ud->bg); GrTextXY(10, 26, "When selected:", ud->fg, ud->bg); GrTextXY(10, 42, " Press R to reverse colors", ud->fg, ud->bg); GrTextXY(10, 58, " Esc to finish", ud->fg, ud->bg); } void paint_tile3(void *data) { UserData *ud; ud = (UserData *)data; GrClearContext(ud->bg); GrTextXY(10, 10, "This is a second active tile,", ud->fg, ud->bg); GrTextXY(10, 26, "When selected:", ud->fg, ud->bg); GrTextXY(10, 42, " Press R to reverse colors", ud->fg, ud->bg); GrTextXY(10, 58, " Esc to finish", ud->fg, ud->bg); } void paint_tile4(void *data) { UserData *ud; ud = (UserData *)data; GrClearContext(ud->bg); GrTextXY(10, 16, "This is a passive tile borderless", ud->fg, ud->bg); } int process_tile_event(void *data, GrEvent *ev) { UserData *ud; GrColor aux; ud = (UserData *)data; if (ev->type == GREV_KEY) { if (ev->p1 == 'r' || ev->p1 == 'R') { aux = ud->fg; ud->fg = ud->bg; ud->bg = aux; ud->gt->p->paintcl(data); return 1; } if (ev->p1 == GrKey_Escape) { return -1; } } return 0; } int main() { #define IDT1 1 #define IDT2 2 #define IDT3 3 #define IDT4 4 GUITile *gt1, *gt2, *gt3, *gt4; UserData ud1, ud2, ud3, ud4; GrEvent ev; int ret; GrSetMode(GR_default_graphics); GUIInit(1, 0); //GrClearScreen(GrAllocColor(0, 100, 0)); gt1 = GUITileCreate(IDT1, GUI_TT_STATICBORDER, 0, 0, GrSizeX(), 50); gt2 = GUITileCreate(IDT2, GUI_TT_ACTIVEBORDER, 0, 50, GrSizeX()/2, GrSizeY()-100); gt3 = GUITileCreate(IDT3, GUI_TT_ACTIVEBORDER, GrSizeX()/2, 50, GrSizeX()/2, GrSizeY()-100); gt4 = GUITileCreate(IDT4, GUI_TT_BORDERLESS, 0, GrSizeY()-50, GrSizeX(), 50); if (gt1 == NULL || gt2 == NULL || gt3 == NULL || gt4 == NULL) exit(1); GUIPanelSetClCallBacks(gt1->p, paint_tile1, NULL); GUIPanelSetUserData(gt1->p, (void *)&ud1); ud1.gt = gt1; ud1.fg = GrWhite(); ud1.bg = GrAllocColor2(0x00AAAA); GUIPanelSetClCallBacks(gt2->p, paint_tile2, process_tile_event); GUIPanelSetUserData(gt2->p, (void *)&ud2); ud2.gt = gt2; ud2.fg = GrWhite(); ud2.bg = GrAllocColor2(0x00AA00); GUIPanelSetClCallBacks(gt3->p, paint_tile3, process_tile_event); GUIPanelSetUserData(gt3->p, (void *)&ud3); ud3.gt = gt3; ud3.fg = GrWhite(); ud3.bg = GrAllocColor2(0x00AA00); GUIPanelSetClCallBacks(gt4->p, paint_tile4, NULL); GUIPanelSetUserData(gt4->p, (void *)&ud4); ud4.gt = gt4; ud4.fg = GrWhite(); ud4.bg = GrAllocColor2(0x555555); GUITileRegister(gt1); GUITileRegister(gt2); GUITileRegister(gt3); GUITileRegister(gt4); GUITilePaint(IDT1); GUITilePaint(IDT2); GUITilePaint(IDT3); GUITilePaint(IDT4); while(1) { GrEventWait(&ev); ret = GUITilesProcessEvent(&ev); if (ret == -1) break; } GUITilesDestroyAll(); GUIEnd(); GrSetMode(GR_default_text); return 0; }
GUI Tiles are containers too, on top of a GUI Panel. But the basic idea of GUI Tiles is to divide the screen area in rectangular areas. Each tile can be passive (only to show information) or active (it can get user input).
Only one of the active tiles have the focus, and you can change it using
the mouse or the Alt+RigtCursor, Alt+LeftCursor keystrokes.
To achieve this functionality, GUI Tiles must be registered using the
"GUITileRegister" function.
#include <stdlib.h> #include <stdio.h> #include <string.h> #include <grgui.h> #include <mgrxcolr.h> #define COMMAND_EXIT 1 #define COMMAND_SWITCH_L1 2 #define COMMAND_SWITCH_L2 3 #define COMMAND_SWITCH_L3 4 #define COMMAND_SWITCH_L4 5 #define COMMAND_GET_DATA 6 char *listopt[5] = { "Primera opción", "Segunda opción", "Tercera opción", "Cuarta opción", "Quinta opción"}; char bdtline1[81], bdtline2[81], bdtline3[81], bdtline4[81]; char *buf_test[4] = {bdtline1, bdtline2, bdtline3, bdtline4}; void add_line_to_buf_test(char *s) { int i; for (i=0; i<3; i++) memcpy(buf_test[i], buf_test[i+1], 81); strncpy(buf_test[3], s, 80); buf_test[3][80] = '\0'; } int process_go_event(GUIGroup *go, GrEvent *ev) { char aux[81]; char *s, *sonoff[4]; char *son = "On"; char *soff = "Off"; int i, status; if (ev->type == GREV_COMMAND) { switch (ev->p1) { case COMMAND_EXIT : return -1; case COMMAND_SWITCH_L1 : GUIGroupSetOn(go, 0, 1); return 1; case COMMAND_SWITCH_L2 : GUIGroupSetOn(go, 1, 1); return 1; case COMMAND_SWITCH_L3 : GUIGroupSetOn(go, 2, 1); return 1; case COMMAND_SWITCH_L4 : GUIGroupSetOn(go, 3, 1); return 1; case COMMAND_GET_DATA : for (i=0; i<4; i++) { status = GUIGroupGetOn(go, i); sonoff[i] = status ? son : soff; } sprintf(aux, "Lights status 1:%s 2:%s 3:%s 4:%s", sonoff[0], sonoff[1], sonoff[2], sonoff[3]); add_line_to_buf_test(aux); s = GUIGroupGetText(go, 5, GR_UTF8_TEXT); sprintf(aux, "Entry: %s", s); add_line_to_buf_test(aux); free(s); GUIGroupRePaintObject(go, 15); return 1; } } if (ev->type == GREV_FCHANGE) { sprintf(aux, "Field changed, p1=%ld, p2=%ld", ev->p1, ev->p2); add_line_to_buf_test(aux); GUIGroupRePaintObject(go, 15); } return GUIGroupProcessEvent(go, ev); } int main() { GUIGroup *go; GrEvent ev; GrSetMode(GR_default_graphics); GUIInit(1, 0); GrGenEgaColorTable(); GrClearContext(EGAC_DARKGRAY); GUIObjectsSetColors(EGAC_BLACK, EGAC_LIGHTGRAY, EGAC_DARKGRAY); go = GUIGroupCreate(17, 160, 70); if (go == NULL) exit(1); GUIObjectSetLight(&(go->o[0]), 0, 0, 0, 80, 32, EGAC_LIGHTGREEN, EGAC_BLACK, "Light 1", 0); GUIObjectSetLight(&(go->o[1]), 1, 80, 0, 80, 32, EGAC_LIGHTGREEN, EGAC_BLACK, "Light 2", 1); GUIObjectSetLight(&(go->o[2]), 2, 160, 0, 80, 32, EGAC_LIGHTRED, EGAC_BLACK, "Light 3", 0); GUIObjectSetLight(&(go->o[3]), 3, 240, 0, 80, 32, EGAC_LIGHTCYAN, EGAC_BLACK, "Light 4", 1); GUIObjectSetLabel(&(go->o[4]), 4, 0, 40, 160, 30, GrNOCOLOR, EGAC_WHITE, "Editable field"); GUIObjectSetEntry(&(go->o[5]), 5, 160, 40, 160, 30, EGAC_WHITE, EGAC_BLACK, 30, "entry field"); GUIObjectSetLabel(&(go->o[6]), 6, 0, 80, 160, 30, GrNOCOLOR, EGAC_WHITE, "List field #1"); GUIObjectSetList(&(go->o[7]), 7, 160, 80, 160, 30, EGAC_WHITE, EGAC_BLACK, (void **)listopt, 5, 3, 1); GUIObjectSetLabel(&(go->o[8]), 8, 0, 120, 160, 30, GrNOCOLOR, EGAC_WHITE, "List field #2"); GUIObjectSetList(&(go->o[9]), 9, 160, 120, 160, 30, EGAC_WHITE, EGAC_BLACK, (void **)listopt, 5, 5, 2); GUIObjectSetButton(&(go->o[10]), 10, 0, 160, 40, 40, EGAC_CYAN, EGAC_WHITE, "L1", COMMAND_SWITCH_L1, 0, 0); GUIObjectSetButton(&(go->o[11]), 11, 40, 160, 40, 40, EGAC_CYAN, EGAC_WHITE, "L2", COMMAND_SWITCH_L2, 0, 0); GUIObjectSetButton(&(go->o[12]), 12, 80, 160, 40, 40, EGAC_CYAN, EGAC_WHITE, "L3", COMMAND_SWITCH_L3, 0, 0); GUIObjectSetButton(&(go->o[13]), 13, 120, 160, 40, 40, EGAC_CYAN, EGAC_WHITE, "L4", COMMAND_SWITCH_L4, 0, 0); GUIObjectSetButton(&(go->o[14]), 14, 160, 160, 160, 40, EGAC_CYAN, EGAC_WHITE, "Get data", COMMAND_GET_DATA, 0, 0); GUIObjectSetText(&(go->o[15]), 15, 0, 210, 320, 80, EGAC_LIGHTGRAY, EGAC_BLACK, (void **)buf_test, 4, GR_ALIGN_LEFT, NULL); GUIObjectSetButton(&(go->o[16]), 16, 80, 300, 160, 40, EGAC_GREEN, EGAC_WHITE, "Exit", COMMAND_EXIT, 0, 0); GUIGroupSetSelected(go, 16, 0); GUIGroupPaint(go); while(1) { GrEventRead(&ev); if ((ev.type == GREV_KEY) && (ev.p1 == GrKey_Escape)) break; if (process_go_event(go, &ev) < 0) break; } GUIGroupDestroy(go); GUIEnd(); GrSetMode(GR_default_text); return 0; }
GUI Objects are widgets, small rectangular objects with some function. There are six objects types in GrGUI:
GUI Objects only can live in a GUI Group. You first create a group and them
defines each element to be a GUI Object of any type. All the objects in a group
are managed as a whole. In this example we create a group with objects of all
types. You can see that it is not necesary to attach a group to a container. We
will do it in the next two examples.
#include <stdlib.h> #include <stdio.h> #include <string.h> #include <grgui.h> #include <mgrxcolr.h> #define COMMAND_EXIT 1 #define COMMAND_LOAD 2 #define COMMAND_SAVE 3 typedef struct { GUITextArea *ta; GUIGroup *go; } UserData; void paint_tl1(void *data) { UserData *ud; ud = (UserData *)data; GrClearContext(EGAC_LIGHTGRAY); GUIGroupPaint(ud->go); } void paint_tl2(void *data) { UserData *ud; ud = (UserData *)data; GUITAReDraw(ud->ta); } int process_tl1_event(void *data, GrEvent *ev) { UserData *ud; GUITAStatus tast; FILE *fin, *fout; char aux[251]; char *s; int len, i; ud = (UserData *)data; if (ev->type == GREV_COMMAND) { switch (ev->p1) { case COMMAND_EXIT : return -1; case COMMAND_LOAD : s = GUIGroupGetText(ud->go, 1, GR_UTF8_TEXT); fin = fopen(s, "r"); free(s); if (fin == NULL) return 1; GUITAHideCursor(ud->ta); while (fgets(aux, 250, fin) != NULL) { len = strlen(aux); if (len>0 && aux[len-1]=='\n') aux[len-1] = '\0'; GUITADrawString(ud->ta, aux, 0, GR_UTF8_TEXT); GUITANewLine(ud->ta); } GUITAShowCursor(ud->ta); fclose(fin); return 1; case COMMAND_SAVE : s = GUIGroupGetText(ud->go, 3, GR_UTF8_TEXT); fout = fopen(s, "w"); free(s); if (fout == NULL) return 1; GUITAGetStatus(ud->ta, &tast); for (i=0; i<tast.nlines; i++) { s = GUITAGetString(ud->ta, i, GR_UTF8_TEXT); if (s == NULL) break; fputs(s, fout); putc( '\n', fout); free(s); } fclose(fout); return 1; } } return GUIGroupProcessEvent(ud->go, ev); } int process_tl2_event(void *data, GrEvent *ev) { UserData *ud; ud = (UserData *)data; return GUITAProcessEvent(ud->ta, ev); } int main() { #define IDT1 1 #define IDT2 2 GUITile *gt1, *gt2; GUITextArea *ta1; GUIGroup *go1; UserData ud; GrEvent ev; int ret; GrSetMode(GR_default_graphics); GUIInit(1, 0); GrGenEgaColorTable(); GUIScrollbarsSetColors(EGAC_LIGHTGRAY, EGAC_DARKGRAY); GUITilesSetColors(EGAC_BLACK, EGAC_LIGHTGRAY, EGAC_YELLOW); GUIObjectsSetColors(EGAC_BLACK, EGAC_LIGHTGRAY, EGAC_DARKGRAY); gt1 = GUITileCreate(IDT1, GUI_TT_ACTIVEBORDER, 0, 0, 168, GrSizeY()); gt2 = GUITileCreate(IDT2, GUI_TT_ACTIVEBWSCB, 168, 0, GrSizeX()-168, GrSizeY()); if (gt1 == NULL || gt2 == NULL) exit(1); GUITileRegister(gt1); GUITileRegister(gt2); go1 = GUIGroupCreate(5, 10, 32); GUIObjectSetButton(&(go1->o[0]), 0, 0, 0, 140, 40, EGAC_GREEN, EGAC_WHITE, "Load File", COMMAND_LOAD, 0, 0); GUIObjectSetEntry(&(go1->o[1]), 1, 0, 44, 140, 30, EGAC_WHITE, EGAC_BLACK, 30, "inputfile"); GUIObjectSetButton(&(go1->o[2]), 2, 0, 84, 140, 40, EGAC_GREEN, EGAC_WHITE, "Save File", COMMAND_SAVE, 0, 0); GUIObjectSetEntry(&(go1->o[3]), 3, 0, 128, 140, 30, EGAC_WHITE, EGAC_BLACK, 30, "outputfile"); GUIObjectSetButton(&(go1->o[4]), 4, 0, 168, 140, 40, EGAC_RED, EGAC_WHITE, "Exit", COMMAND_EXIT, 0, 0); GUIGroupSetSelected(go1, 0, 0); GUIGroupSetPanel(go1, gt1->p); ta1 = GUITACreate(gt2->p, NULL, 10000); if (ta1 == NULL) exit(1); GUITASetBgColor(ta1, EGAC_DARKGRAY); GUITASetTextColors(ta1, EGAC_WHITE, EGAC_DARKGRAY); GUITASetCursorColor(ta1, EGAC_YELLOW); GUITAClear(ta1); GUIPanelSetClCallBacks(gt1->p, paint_tl1, process_tl1_event); GUIPanelSetClCallBacks(gt2->p, paint_tl2, process_tl2_event); GUIPanelSetUserData(gt1->p, (void *)&ud); GUIPanelSetUserData(gt2->p, (void *)&ud); ud.ta = ta1; ud.go = go1; GUITilePaint(IDT1); GUITilePaint(IDT2); GUITAShowCursor(ta1); while(1) { GrEventRead(&ev); ret = GUITilesProcessEvent(&ev); if (ret == -1) break; } GUIGroupDestroy(go1); GUITADestroy(ta1); GUITilesDestroyAll(); GUIEnd(); GrSetMode(GR_default_text); return 0; }
In this example we divide the screen with two active GUI Tiles, we attach a GUI Group of object to one Tile and a GUI TextArea to the other one.
A GUI TextArea is a window over a editable text. So in our present example
we have a simple text editor.
#include <stdlib.h> #include <stdio.h> #include <string.h> #include <grgui.h> #include <mgrxcolr.h> #define COMMAND_OK 1 #define COMMAND_SWITCH_L1 2 #define COMMAND_SWITCH_L2 3 #define COMMAND_SWITCH_L3 4 #define COMMAND_SWITCH_L4 5 #define COMMAND_GET_DATA 6 char *listopt[5] = { "Primera opción", "Segunda opción", "Tercera opción", "Cuarta opción", "Quinta opción"}; char bdtline1[81], bdtline2[81], bdtline3[81], bdtline4[81]; char *buf_test[4] = {bdtline1, bdtline2, bdtline3, bdtline4}; void print_line(char *s) { #define LINE_HIGH 16 static int ypos = 10; if (ypos >= GrMaxY() - LINE_HIGH) { GrClearContext(GrBlack()); ypos = 10; } GrTextXY(10, ypos, s, GrWhite(), GrBlack()); ypos += LINE_HIGH; } void add_line_to_buf_test(char *s) { int i; for (i=0; i<3; i++) memcpy(buf_test[i], buf_test[i+1], 81); strncpy(buf_test[3], s, 80); buf_test[3][80] = '\0'; } int process_dlg_event(void *udata, GrEvent *ev) { GUIDialog *d = (GUIDialog *)udata; GUIGroup *go = (GUIGroup *)(d->exdata); char aux[81]; char *s, *sonoff[4]; char *son = "On"; char *soff = "Off"; int i, status; if (ev->type == GREV_COMMAND) { switch (ev->p1) { case COMMAND_OK : return -1; case COMMAND_SWITCH_L1 : GUIGroupSetOn(go, 0, 1); return 1; case COMMAND_SWITCH_L2 : GUIGroupSetOn(go, 1, 1); return 1; case COMMAND_SWITCH_L3 : GUIGroupSetOn(go, 2, 1); return 1; case COMMAND_SWITCH_L4 : GUIGroupSetOn(go, 3, 1); return 1; case COMMAND_GET_DATA : for (i=0; i<4; i++) { status = GUIGroupGetOn(go, i); sonoff[i] = status ? son : soff; } sprintf(aux, "Lights status 1:%s 2:%s 3:%s 4:%s", sonoff[0], sonoff[1], sonoff[2], sonoff[3]); add_line_to_buf_test(aux); s = GUIGroupGetText(go, 5, GR_UTF8_TEXT); sprintf(aux, "Entry: %s", s); add_line_to_buf_test(aux); free(s); GUIGroupRePaintObject(go, 15); return 1; } } if (ev->type == GREV_FCHANGE) { sprintf(aux, "Field changed, p1=%ld, p2=%ld", ev->p1, ev->p2); add_line_to_buf_test(aux); GUIGroupRePaintObject(go, 15); } return GUIGroupProcessEvent(go, ev); } int main() { GUIGroup *go; GUIDialog *d; GrEvent ev; char s[81]; int result, i; GrSetMode(GR_default_graphics); GUIInit(1, 0); GrGenEgaColorTable(); GUIObjectsSetColors(EGAC_BLACK, EGAC_LIGHTGRAY, EGAC_DARKGRAY); GUIDialogsSetColors(EGAC_BLACK, EGAC_YELLOW, EGAC_BLUE, EGAC_WHITE); go = GUIGroupCreate(17, 160, 70); if (go == NULL) exit(1); GUIObjectSetLight(&(go->o[0]), 0, 0, 0, 80, 32, EGAC_LIGHTGREEN, EGAC_BLACK, "Light 1", 0); GUIObjectSetLight(&(go->o[1]), 1, 80, 0, 80, 32, EGAC_LIGHTGREEN, EGAC_BLACK, "Light 2", 1); GUIObjectSetLight(&(go->o[2]), 2, 160, 0, 80, 32, EGAC_LIGHTRED, EGAC_BLACK, "Light 3", 0); GUIObjectSetLight(&(go->o[3]), 3, 240, 0, 80, 32, EGAC_LIGHTCYAN, EGAC_BLACK, "Light 4", 1); GUIObjectSetLabel(&(go->o[4]), 4, 0, 40, 160, 30, GrNOCOLOR, EGAC_WHITE, "Editable field"); GUIObjectSetEntry(&(go->o[5]), 5, 160, 40, 160, 30, EGAC_WHITE, EGAC_BLACK, 30, "entry field"); GUIObjectSetLabel(&(go->o[6]), 6, 0, 80, 160, 30, GrNOCOLOR, EGAC_WHITE, "List field #1"); GUIObjectSetList(&(go->o[7]), 7, 160, 80, 160, 30, EGAC_WHITE, EGAC_BLACK, (void **)listopt, 5, 3, 1); GUIObjectSetLabel(&(go->o[8]), 8, 0, 120, 160, 30, GrNOCOLOR, EGAC_WHITE, "List field #2"); GUIObjectSetList(&(go->o[9]), 9, 160, 120, 160, 30, EGAC_WHITE, EGAC_BLACK, (void **)listopt, 5, 5, 2); GUIObjectSetButton(&(go->o[10]), 10, 0, 160, 40, 40, EGAC_CYAN, EGAC_WHITE, "L1", COMMAND_SWITCH_L1, 0, 0); GUIObjectSetButton(&(go->o[11]), 11, 40, 160, 40, 40, EGAC_CYAN, EGAC_WHITE, "L2", COMMAND_SWITCH_L2, 0, 0); GUIObjectSetButton(&(go->o[12]), 12, 80, 160, 40, 40, EGAC_CYAN, EGAC_WHITE, "L3", COMMAND_SWITCH_L3, 0, 0); GUIObjectSetButton(&(go->o[13]), 13, 120, 160, 40, 40, EGAC_CYAN, EGAC_WHITE, "L4", COMMAND_SWITCH_L4, 0, 0); GUIObjectSetButton(&(go->o[14]), 14, 160, 160, 160, 40, EGAC_CYAN, EGAC_WHITE, "Get data", COMMAND_GET_DATA, 0, 0); GUIObjectSetText(&(go->o[15]), 15, 0, 210, 320, 80, EGAC_LIGHTGRAY, EGAC_BLACK, (void **)buf_test, 4, GR_ALIGN_LEFT, NULL); GUIObjectSetButton(&(go->o[16]), 16, 80, 300, 160, 40, EGAC_GREEN, EGAC_WHITE, "Exit", COMMAND_OK, 0, 0); GUIGroupSetSelected(go, 16, 0); d = GUIGroupDialogCreate("Test various objects", go, process_dlg_event); if (d == NULL) exit(1); print_line("Press D to run dialog"); print_line("Esc to quit"); while(1) { GrEventRead(&ev); if (ev.type == GREV_KEY) { if (ev.p1 == GrKey_Escape) break; if (ev.p1 == 'd' || ev.p1 == 'D') { result = GUIDialogRun(d); sprintf(s,"Dialog returned %d, Text object contens:", result); print_line(s); for (i=0; i<4; i++) print_line(buf_test[i]); } } } GUIDialogDestroy(d); GUIGroupDestroy(go); GUIEnd(); GrSetMode(GR_default_text); return 0; }
Now we reuse the GUi Group we create in the example 8 and attach it to a dialog
to show we can have complex dialogs with GrGUI.
#include <stdlib.h> #include <stdio.h> #include <grgui.h> #include "mgrxcolr.h" #if defined(__MSDOS__) || defined(__WIN32__) #define JPGIMGBG "..\\testimg\\jpeg4.jpg" #else #define JPGIMGBG "../testimg/jpeg4.jpg" #endif int main() { char *abouttext[4] = { "Welcome to MGRX and GrGUI", "MGRX is a small C 2D graphics library", "and GrGUI a miniGUI on top of MGRX", "visit mgrx.fgrim.com for more info"}; GrContext *globctx; GrSetMode(GR_width_height_bpp_graphics, 640, 480, 32); GrGenWebColorTable(); GrSetFontPath("../fonts/;./"); GUIInit(1, 1); GUIObjectsSetColors(WEBC_KHAKI, WEBC_PERU, WEBC_SIENNA); GUIObjectsSetFontByName("tmgrx16b.fnt"); GUIDialogsSetColors(WEBC_BLACK, WEBC_ORANGE, WEBC_MAROON, WEBC_ANTIQUEWHITE); GUICDialogsSetColors(WEBC_TAN, WEBC_BLACK); GUIDialogsSetTitleFontByName("ncen40bi.fnt"); GUICDialogsSetFontByName("tmgrx18b.fnt"); globctx = GUIGetGlobalContext(); GrSetContext(globctx); if (GrNumColors() > 256 && GrJpegSupport()) { GrLoadContextFromJpeg(NULL, JPGIMGBG, 1); } else { GrFilledBox(0, 0, GrMaxX(), GrMaxY(), WEBC_GOLDENROD); } GUIGlobalBltRectToScreen(0, 0, GrScreenX()-1, GrScreenY()-1); GUICDialogInfo("Hello GrGUI", (void **)abouttext, 4, "Okey"); GUIEnd(); GrSetMode(GR_default_text); return 0; }
Until now sometimes we have left the default black and whiite colors, sometime we have used GrGUI functions to set the default colors of some elements. In all cases we have left the default font.
In this last example we return to our first hello GrGUI example but changing default colors and fonts. We draw a background image too and activate the double buffer (second parameter of "GUIInit" function).
While we use the GUI functions to do the drawing there are no difference, but in other case we have to use special code. You can notice that to draw the image background instead doing it in the Screen context we use the "GUIGetGlobalContext" to get the memory context GrGUI is using to draw, and after putting our image in, we bitblt to the real Screen using "GUIGlobalBltRectToScreen".
So it is different to code with or without double buffer.
The good news is that "GUIGetGlobalContext" returns the Screen context if GrGUI
isn't using double buffer and "GUIGlobalBltRectToScreen" doesn't do anything in that
case. So we can code as the double buffer is ever set and it works ok without double
buffer.
I think the eleven examples covers most of the GrGUI functionality, but GrGUI is a small GUI, you can use the "grgui.h" main include file as a reference, or even read the source code if nedeed.
A big example is the "demintl2.c" program, it use is mainly to test the international features of MGRX, but it makes extensive use of GrGUI too.
Enjoy.