The GUI is intended for PC and/or Console Games.
Focus was mostly on keeping everything simple, easy to use, fast, and flexible (like having skin and ttf support).
The performance is ~700 fps for 20 windows and 100 fps for 100 windows on a Notebook PC (NVidia GTX 765 / Core i7 2.4).
The source (MIT license) can be downloaded from GitHUB or here.
Creating and opening a window:
gui.window["mywin"]=Gui::Window(
"Hello World"
,100,100,350,250);
Adding a button:
gui.window["mywin"].button[
"Ok1"
]=Gui::Button(
"OK"
,20,100,60,20);
Adding a callback to close the window when pushing the button:
gui.window["mywin"].button["Ok1"].callback_pressed=
[](Gui::Window *w,Gui::Button* b,int index) { w->close(); };
Here two screenshots with different skins
( VS2012 / OpenGL / GLUT based / MIT license )
Features:
- Advanced skin scaling: define how the inner and borders of a texture shall be scaled ( simple scale, repeating inner pattern or constant outer frame, carried out by 3 Shader types)
- UTF8 Support
- TTF Support
- Easy skinning support
- Alpha support (RGBA), so you can have transparent windows/widgets etc
- direct access to all controls and their members
- Non-blocking visualization
- Callback support
- Different from most GUIs, Widgets are stored directly inside the GUI rather having local variables pointing to instances of an overwritten class.
- Directory: Controls are stored and accessed by a tree structure that is rendered every time like a scene graph
- No event loop, show function or update function
- No need to have a timer that calls an update function
- Lightweight: Only Depends on GL and libFreetype and DevIL
- Can easily be used in SDL, Qt and other environments. Just plug in the keyboard and mouse events and call the gui's draw function from your main render loop.
- Config file support for default variables (font,padding etc) and skin textures+scaling parameters
- Only 2000 lines of code for the entire Gui class including all widgets. Therefore easy to modify and extend.
- Comes with a simple file-browser (See screenshot1)
- MIT license
- Multi-screen support (same as multiple Desktops). You can have a title screen, loading screen, ingame screen etc. Simply create them at the beginning and the switch between by setting gui.active_screen=number;
- Window manager
- Context menu
- Custom mouse pointer
- Controls have support for custom user variables like control.var.string["myvar"]="hello"
Available classes for controls:
- Window class
- Label class
- Button class
- Combo class
- Radio class
- Tab class
- Textedit class
- Slider class
- CheckBox class
- Menu class
What the GUI is good for:
- PC/Console Games
- Simple menus
What the GUI might not be suited for
- Complex GUIs that need tree-views , docking ,drag & drop etc
- Mobile Devices ; perhaps too slow (needs testing) and also the render code needs to become OpenGL ES
Current Limitations (might be solved in the future)
- Uses old OpenGL (glMatrix etc)
- Usually one or two render calls per widget rather than one opengl render call for the entire gui.
- 2D windows/widgets only. You could overwrite the render shader to get 3D, but you would have to map 2D mouse coordinates to 3D then by yourself.
- No animations (you could create them yourself by updating the textures accordingly or modifying the shader that render the widgets)
- Only one font size and one font type
- The GUI class is a singleton
- The TextEdit does not support arbitrary cursor position or ctrl+c/v/x operations
- Drag and Drop is not supported
- No automatic window layout
- No docking
- No Ressource files (yet)
- No treeview class
- No caching of rendered labels, geometry, etc. Therefore every frame, the entire tree is traversed and every single character of a rendered text is processed for rendering.
The code used to create window + widgets + menu in screenshot2 is the following:
gui.init( Gui::Flags::CONTEXT_MENU | Gui::Flags::CUSTOM_MOUSE ,
"../data/gui_global.txt" ,
"../data/gui_skin.txt");
// Main Window
Gui::Window w=Gui::Window("Hello World",100,100,350,250);
// Add Simple Label to Window
w.label["l"]=Gui::Label("Label",20,70,100,20);
// Add Simple Button to Window
w.button["Ok1"]=Gui::Button("OK",20,100,60,20);
w.button["Ok1"].callback_pressed=
[](Gui::Window *window,Gui::Button* control,int index)
{
window->close();
};
w.button["Ok2"]=Gui::Button("",220,100,100,100);
w.button["Ok2"].skin=Skin( "../data/smiley.png",
"../data/smileybw.png",
"../data/smiley2.png");
// Add Simple Combo to Window
w.combo ["cb"]=Gui::Combo(120,100,60,20);
w.combo ["cb"].add_item("test");
w.combo ["cb"].add_item(L"東京");
w.combo ["cb"].callback_selected=
[](Gui::Window *w,Gui::Button* control,int index) // text entered callback example
{
Gui::Combo &c=*(Gui::Combo*)control;
w->label["l"].text=Gui::String( c.selected ) + " selected";
w->label["l"].textcolor=vec4f(1,0,0,1);
};
// Add Text Edit to Window
w.textedit["txt"]=Gui::TextEdit(10,"text",20,150,160,20);
w.textedit["txt"].callback_text_entered=
[](Gui::Window *w,Gui::Button* control,int index) // text entered callback example
{
Gui::TextEdit &t=*(Gui::TextEdit*)control;
w->title.text=t.text;
};
// Add Radio
w.radio["rad"]=Gui::Radio(20,190,20,20); // first button
w.radio["rad"].add_item(50,190); // second button
w.radio["rad"].add_item(80,190); // third button
w.radio["rad"].callback_pressed=
[](Gui::Window *w,Gui::Button* control,int index)
{
w->x+=10;
};
// Add Slider
w.slider["s1"]=Gui::Slider( 0,200,100, /* min,max,default*/
20,220,160,20); /* window x,y,sx,sy */
w.slider["s1"].callback_pressed=
[](Gui::Window *w,Gui::Button* control,int index)
{
Gui::Slider &b=*((Gui::Slider*) control);
w->button["Ok1"].text=Gui::String(int(b.val));
};
w.slider["s2"]=Gui::Slider( 0,200,100, /* min,max,default*/
350,100,20,160, /* window x,y,sx,sy */
Gui::Slider::VERTICAL );
// Add File Menu to Window
Gui::Menu m=Gui::Menu("File",/* x,y,sx,sy */ 9,39,50,20, /* menuwidth */ 100);
m.add_item("Load",
[](Gui::Window *window,Gui::Button* control,int index) // menu button callback
{
gui.screen[0].window["filebrowser"]= // open file dialog
gui_file_dialog( "Load SaveGame" , "Load" , "Cancel" ,
/*get_current_dir()*/ "..\\data\\win8",".png", 100,100,
// file dialog ok button callback
[](Gui::Window *w,Gui::Button* b,int index)
{
MessageBoxA(0,
w->textedit["Filename"].text.c_str() ,
w->label["dir"].text.c_str() ,0);
w->close();
}
);
});
m.add_item("Close",
[](Gui::Window *w,Gui::Button* control,int index) // menu button callback
{
w->parent->close(); // close window
});
m.add_menu("submenu");
m.window.menu[0].add_item("test1");
m.window.menu[0].add_item("test2");
w.menu["menu"]=m;
// Add new Window to Screen 0 (default)
gui.screen[0].window["hello1"]=w; // finally put window on the screen (1st copy)
// Modify and Add new Window to Screen 0 (default)
w.move(500,100);
w.resize(400,300);
w.minsize(150,150);
gui.screen[0].window["hello2"]=w; // finally put window on the screen (2nd copy)
// -------------------------------------------------------------------------
// also use our previous menu as context menu and file menu
// Note : "context_menu" is reserved for the context menu
// all other id names are common menus on the background
gui.screen[0].menu["context_menu"]=m;
gui.screen[0].menu["file"]=m;
gui.screen[0].menu["file"].y=5;
// -------------------------------------------------------------------------
// Add Button to Background
gui.dialog["sample"]=w; // store for later use in the callback
gui.screen[0].button["more"]=Gui::Button("More Windows Plz!!",50,50,200,20);
gui.screen[0].button["more"].callback_pressed=
[](Gui::Window *w,Gui::Button* control,int index) // menu button callback
{
int id=gui.screen[0].window.add(gui.dialog["sample"]);
gui.screen[0].window[id].x= timeGetTime() % (int)gui.screen_resolution_x;
gui.screen[0].window[id].y= timeGetTime() % (int)gui.screen_resolution_y;
};
// Add Tabbed Window to Background
Gui::Tab t=Gui::Tab("Win1",350,150,300,200,50,20);;
t.add_tab("Win2");
t.add_tab("Win3");
t.add_tab("Win4");
loopi(0,4)
{
t.window[i].button.add(Gui::Button("OK",20+i*10,20,60));
t.window[i].label["lab"] =Gui::Label("some text",20+i*20,90,100);
t.window[i].button["test2"]=Gui::Button("OK",100+i*10,20,60);
t.window[i].button["test3"]=Gui::Button("OK",50+i*10,50,60);
}
t.flags=Gui::Tab::MOVABLE; // Make it movable
gui.screen[0].tab["mytab"]=t;
Main reason for the development of the GUI was, that after trying several gui's more or less suited for games (LibRocket, Qt, NVWidgets, CEgui, etc), I found that none of them was really light weight, easy to use and fully skinable at the same time. So the solution was to create one which is exactly so.