一个用于 Win32、X11 和 Essence 的单个头文件的 GUI 库。
A barebones single-header GUI library for Win32, X11 and Essence.
Designer, https://gitlab.com/nakst/essence/-/blob/master/util/designer2.cpp
GDB frontend, https://github.com/nakst/gf/
Various utility applications:
Update luigi_example.c to #define UI_WINDOWS at the top of the file, and then run the following command in a Visual Studio command prompt:
cl /O2 luigi_example.c user32.lib gdi32.lib shell32.libUpdate luigi_example.c to #define UI_LINUX at the top of the file, and then run the following command in Bash:
gcc -O2 luigi_example.c -lX11 -lm -o luigiIf you want to use FreeType for font rendering, pass the additional arguments to gcc:
-lfreetype -D UI_FREETYPE -I Then add, after the call to UIInitialise,
UIFontActivate(UIFontCreate("font_path.ttf", 11 /* font size */));As with other single-header libraries, to use it in your project define UI_IMPLEMENTATION in exactly one translation unit where you include the header.
Furthermore, everytime you include the header, you must either define UI_WINDOWS or UI_LINUX to specify the target platform.
To initialise the library, call UIInitialise. You can then create a window using UIWindowCreate and populate it using the UI...Create functions.
Once you're ready, call UIMessageLoop, and input messages will start being processed.
Windows are built up of elements, which are allocated and initialised by UI...Create functions. These functions all return a pointer to the allocated element.
At the start of every element is a common header of type UIElement, contained in the field e.
When you create an element, you must specify its parent element and its flags.
Each element determines the position of its children, and every element is clipped to its parent (i.e. it cannot draw outside the bounds of the parent).
The library uses a message-based system to allow elements to respond to events and requests.
The enumeration UIMessage specifies all the different messages that can be sent to an element using the UIElementMessage function.
A message is passed with two parameters, an integer di and a pointer dp, and the element receiving the message must return an integer in response.
If the meaning of the return value is not specified, or the element does not handle the message, it should return 0.
After ensuring the element has not been marked for deletion,
UIElementMessage will first try sending the message to the messageUser function pointer in the UIElement header.
If this returns 0, then the message will also be sent to the messageClass function pointer.
The UI...Create functions will set the messageClass function pointer, and the user may optionally set the messageUser to also receive messages sent to the element.
For example, the messageClass function set by UIButtonCreate will handle drawing the button when it receives the UI_MSG_PAINT message.
The user will likely want to set messageUser so that they can receive the UI_MSG_CLICKED message, which indicates that the button has been clicked.
The following source code demonstrates how to create an empty window.
// Define UI_LINUX instead if you're on Linux.
#define UI_WINDOWS
// Put the library implementation in this translation unit.
#define UI_IMPLEMENTATION
#include "luigi.h"
// Use main() instead if you're on Linux.
int WinMain(HINSTANCE instance, HINSTANCE previousInstance, LPSTR commandLine, int showCommand) {
// Initialise the library.
UIInitialise();
// Create a window.
UIWindow *window = UIWindowCreate(0, 0, "My First Application", 640, 480);
// Process input messages from the operating system.
return UIMessageLoop();
}Since we haven't added anything to the window, its contents will be uninitialized - so don't worry if you see some random pixels.
To start adding elements to the window, we first need to add a panel which will be responsible for laying out the other elements in the window.
…We can now add some elements to the panel.
// Global variables:
UIButton *button;
UIColorPicker *colorPicker;
UIGauge *gauge;
UISlider *slider;
UISpacer *spacer;
UILabel *label;
UITextbox *textbox;
...
UIPanel *panel = UIPanelCreate(&window->e, UI_PANEL_GRAY | UI_PANEL_MEDIUM_SPACING);
button = UIButtonCreate(&panel->e, 0, "Push", -1);
colorPicker = UIColorPickerCreate(&panel->e, 0);
gauge = UIGaugeCreate(&panel->e, 0);
slider = UISliderCreate(&panel->e, 0);
spacer = UISpacerCreate(&panel->e, 0, 0 /* width */, 20 /* height */);
label = UILabelCreate(&panel->e, 0, "Label", -1);
textbox = UITextboxCreate(&panel->e, 0);Let's add some interactivity to the interface. Set the message callbacks for the button, slider and textbox. We can process input messages in these callbacks, and respond to them however we want.
button->e.messageUser = ButtonMessage;
slider->e.messageUser = SliderMessage;
textbox->e.messageUser = TextboxMessage;In the button's callback, we'll change the color in the color picker to white when the button is clicked.
int ButtonMessage(UIElement *element, UIMessage message, int di, void *dp) {
if (message == UI_MSG_CLICKED) {
colorPicker->saturation = 0;
colorPicker->value = 1;
UIElementRefresh(&colorPicker->e); // Update and repaint the color picker.
}
return 0;
}In the slider's callback, we'll make the gauge match the slider's position.
int SliderMessage(UIElement *element, UIMessage message, int di, void *dp) {
if (message == UI_MSG_VALUE_CHANGED) {
gauge->position = slider->position;
UIElementRefresh(&gauge->e);
}
return 0;
}Finally, in the textbox's callback, we'll make the label match the textbox's contents.
int TextboxMessage(UIElement *element, UIMessage message, int di, void *dp) {
if (message == UI_MSG_VALUE_CHANGED) {
UILabelSetContent(label, textbox->string, textbox->bytes);
UIElementRefresh(&label->e);
// The label's size might have changed,
// so we need to refresh the parent panel, to update its layout.
UIElementRefresh(label->e.parent);
}
return 0;
}This contains 4 integers, l, r, t and b which represent the left, right, top and bottom edges of a rectangle.
Usually, the coordinates are in pixels relative to the top-left corner of the relevant window.
The common header for all elements.
struct UIElement {
uint64_t flags;
struct UIElement *parent;
struct UIElement *next;
struct UIElement *children;
struct UIWindow *window;
UIRectangle bounds, clip, repaint;
void *cp;
int (*messageClass)(struct UIElement *element, UIMessage message, int di, void *dp);
int (*messageUser)(struct UIElement *element, UIMessage message, int di, void *dp);
};flags contains a bitset of flags for the element. The first 16-bits are specific to each type of element. The upper 16-bits are common to all elements.
Here are the common flags are available:
UI_ELEMENT_V_FILL is a hint to the parent element that this element should take up all available vertical space.UI_ELEMENT_H_FILL is a hint to the parent element that this element should take up all available horizontal space.UI_ELEMENT_REPAINT marks the element for repainting. Do not set directly; see UIElementRepaint.UI_ELEMENT_DESTROY marks the element to be destroyed. Do not set directly; see UIElementDestroy.UI_ELEMENT_DISABLED marks the element as disabled. It will not receive input events.UI_ELEMENT_HIDE marks the element as hidden. It will not receive input events, be drawn, or take up space in the parent's layout.UI_ELEMENT_TAB_STOP marks the element as a tab stop. The user may focus it using the tab key.UI_ELEMENT_PARENT_PUSH automatically adds the element to the parent stack. See UIParentPush.UI_ELEMENT_NON_CLIENT indicates the element behaves less like a child of its parent, but rather is integral to the existence of its parent. For example, scrollbars in a table will be marked as non-client. This flag has several effects: UIElementDestroyDescendents will not destroy non-client elements; the UI_MSG_CLIENT_PARENT message will not be sent to the parent during its creation; and panels will not include it in their layout.parent contains a pointer to the element's parent. next contains a pointer to the element's sibling.
children contains a pointer to the element's first child (children are linked by the next pointer).
window contains a pointer to the window that contains the element.
bounds contains the element's bounds, expressed in pixels relative to the top-left corner of the containing window. clip gives the clip region in a similar fashion. Do not set either of these directly; instead, use UIElementMove.
cp is a context pointer available for the user.
messageClass and messageUser contain function pointers to the element's message handlers.
messageClass is set when the element is first created, and has lower priority than messageUser when receiving messages.
messageUser can be optionally set by the user of the element to inspect and handle its messages.
If messageUser returns a non-zero value, then messageClass will not receive the message.
If messageUser is NULL, then messageClass will always receive the message.
Do not call these directly; see UIElementMessage.
……No, this would require a rewrite in JavaScript, which I do not currently plan to do.
暂无开放 Issues,或尚未同步最近议题。