diff --git a/demo.c b/demo.c index 5fca676..fe14f7e 100644 --- a/demo.c +++ b/demo.c @@ -20,24 +20,27 @@ ** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ** IN THE SOFTWARE. */ -#include + #include #include +#include +#include #include int checked = 0; float slider = 0.0; float number = 0.0; -char buf[256] = {0}; +char buf_a[256] = {0}; +char buf_b[256] = {0}; int main(void) { InitWindow(800, 600, "demo"); SetTargetFPS(60); - mu_Context *ctx = malloc(sizeof(mu_Context)); + mu_Context* ctx = smolui_mu_context_new(); mu_init(ctx); - smolui_setup_font_ex(ctx, NULL); + smolui_setup_font(ctx, NULL); while (!WindowShouldClose()) { BeginDrawing(); @@ -46,41 +49,62 @@ int main(void) { smolui_handle_input(ctx); mu_begin(ctx); - if (mu_begin_window(ctx, "Hello", mu_rect(20, 20, 400, 300))) { - mu_text(ctx, "text"); - - mu_checkbox(ctx, "checkbox", &checked); - - mu_slider(ctx, &slider, 0.0, 100.0); - - mu_number(ctx, &number, 2.0); - - mu_label(ctx, "Hello, raylib"); - - if (mu_header(ctx, "Header")) { + if (mu_begin_window_ex(ctx, "Hello", mu_rect(20, 20, 800 - 2 * 20, 600 - 2 * 20), MU_OPT_NOCLOSE)) { + mu_text(ctx, "some text"); + mu_label(ctx, "label"); + if (mu_button(ctx, "button")) { + puts("button pressed"); + } + if (mu_button_ex(ctx, "button ex", MU_ICON_MAX, MU_OPT_ALIGNCENTER)) { + puts("button (ex) pressed"); + } + if (mu_checkbox(ctx, "checkbox", &checked)) { + puts("checkbox interaction"); + } + if (mu_textbox(ctx, buf_a, sizeof(buf_a))) { + puts("textbox interaction"); + } + int textbox_ex_res = mu_textbox_ex(ctx, buf_b, sizeof(buf_b), MU_OPT_ALIGNCENTER); + if (textbox_ex_res) { + puts("textbox interaction"); + if (textbox_ex_res & MU_RES_SUBMIT) { + puts("\ttextbox submit"); + } + if (textbox_ex_res & MU_RES_CHANGE) { + puts("\ttextbox change"); + } + } + if (mu_slider(ctx, &slider, 0.0, 100.0)) { + puts("slider interaction"); + } + if (mu_number(ctx, &number, 5.0)) { + puts("number interaction"); + } + if (mu_header(ctx, "header")) { mu_text(ctx, "text under header"); } - - if (mu_begin_treenode(ctx, "Treenode")) { + if (mu_header_ex(ctx, "header ex", MU_OPT_EXPANDED)) { + mu_text(ctx, "text under header (ex)"); + } + if (mu_begin_treenode(ctx, "treenode")) { mu_text(ctx, "text under treenode"); mu_end_treenode(ctx); } - - if (mu_button(ctx, "The button")) { + if (mu_begin_treenode_ex(ctx, "treenode ex", MU_OPT_EXPANDED)) { + mu_text(ctx, "text under treenode (ex)"); + mu_end_treenode(ctx); + } + if (mu_button(ctx, "open popup")) { mu_open_popup(ctx, "popup"); } - if (mu_begin_popup(ctx, "popup")) { - mu_label(ctx, "This is a popup"); + mu_text(ctx, "text inside popup"); mu_end_popup(ctx); } - mu_begin_panel(ctx, "panel"); - mu_text(ctx, "text under panel"); + mu_text(ctx, "text inside panel"); mu_end_panel(ctx); - mu_textbox(ctx, buf, sizeof(buf)); - mu_end_window(ctx); } @@ -89,7 +113,7 @@ int main(void) { EndDrawing(); } - free(ctx); + smolui_mu_context_del(ctx); CloseWindow(); return 0; } diff --git a/demo.sunder b/demo.sunder index 84d8d31..3b04ef4 100644 --- a/demo.sunder +++ b/demo.sunder @@ -6,68 +6,89 @@ import "raylib"; import "microui.sunder"; import "smolui.sunder"; -var checked: sint = 0; -var slider: float = 0.0; -var number: float = 0.0; -var buf = (:[256]byte)[0...]; - func main() void { InitWindow(800, 600, startof("demo")); defer CloseWindow(); SetTargetFPS(60); - var ctx = smolui::mu_context_new(); - defer smolui::mu_context_del(ctx); - mu_init(ctx); - smolui::setup_font_ex(ctx, std::ptr[[Font]]::NULL); + var ui = smolui::context::init(std::ptr[[Font]]::NULL); + + var textbuf_a = std::string::init_from_str("textbox a"); + var textbuf_b = std::string::init_from_str("textbox b"); + defer textbuf_a.fini(); + defer textbuf_b.fini(); + var checked: bool = false; + var slider: float = 0.0; + var number: float = 0.0; for not WindowShouldClose() { BeginDrawing(); ClearBackground(BLACK); - smolui::handle_input(ctx); - mu_begin(ctx); - - if mu_begin_window(ctx, startof("Hello"), (:mu_Rect){.x = 20, .y = 20, .w = 400, .h = 300}) != 0 { - mu_text(ctx, startof("text")); - - mu_checkbox(ctx, startof("checkbox"), &checked); - - mu_slider(ctx, &slider, 0.0, 100.0); - - mu_number(ctx, &number, 2.0); + ui.handle_input(); - mu_label(ctx, startof("Hello, raylib")); - - if mu_header(ctx, startof("Header")) != 0 { - mu_text(ctx, startof("text under header")); + ui.begin(); + if ui.begin_window_ex("window", (:smolui::rect){.x = 20, .y = 20, .w = 800 - 2 * 20, .h = 600 - 2 * 20}, smolui::OPT_NOCLOSE) { + ui.text("some text"); + ui.label("label"); + if ui.button("button"){ + std::print_line(std::out(), "button pressed"); } - - if mu_begin_treenode(ctx, startof("Treenode")) != 0 { - mu_text(ctx, startof("text under treenode")); - mu_end_treenode(ctx); + if ui.button_ex("button ex", smolui::ICON_MAX, smolui::OPT_ALIGNCENTER){ + std::print_line(std::out(), "button (ex) pressed"); } - - if mu_button(ctx, startof("The button")) != 0 { - mu_open_popup(ctx, startof("popup")); + if ui.checkbox("checkbox", &checked) { + std::print_line(std::out(), "checkbox interaction"); } - - if mu_begin_popup(ctx, startof("popup")) != 0 { - mu_label(ctx, startof("This is a popup")); - mu_end_popup(ctx); + if ui.textbox(&textbuf_a) { + std::print_line(std::out(), "textbox interaction"); } + var textbox_ex_res = ui.textbox_ex(&textbuf_b, smolui::OPT_ALIGNCENTER); + if textbox_ex_res != 0 { + std::print_line(std::out(), "textbox (ex) interaction"); + if (textbox_ex_res & smolui::RES_SUBMIT) != 0 { + std::print_line(std::out(), "\ttextbox submit"); + } + if (textbox_ex_res & smolui::RES_CHANGE) != 0 { + std::print_line(std::out(), "\ttextbox change"); + } + } + if ui.slider(&slider, 0.0, 100.0) { + std::print_line(std::out(), "slider interaction"); + } + if ui.number(&number, 5.0) { + std::print_line(std::out(), "number interaction"); + } + if ui.header("header") { + ui.text("text under header"); + } + if ui.header_ex("header ex", smolui::OPT_EXPANDED) { + ui.text("text under header (ex)"); + } + if ui.begin_treenode("treenode") { + ui.text("text under treenode"); + ui.end_treenode(); + } + if ui.begin_treenode_ex("treenode ex", smolui::OPT_EXPANDED) { + ui.text("text under treenode (ex)"); + ui.end_treenode(); + } + if ui.button("open popup") { + ui.open_popup("popup"); + } + if ui.begin_popup("popup") { + ui.text("text inside popup"); + ui.end_popup(); + } + ui.begin_panel("panel"); + ui.text("text inside panel"); + ui.end_panel(); - mu_begin_panel(ctx, startof("panel")); - mu_text(ctx, startof("text under panel")); - mu_end_panel(ctx); - - mu_textbox(ctx, &buf[0], (:sint)countof(buf)); - - mu_end_window(ctx); + ui.end_window(); } + ui.end(); - mu_end(ctx); - smolui::render(ctx); + ui.render(); EndDrawing(); } } diff --git a/microui.sunder b/microui.sunder index 9531a32..8d9114c 100644 --- a/microui.sunder +++ b/microui.sunder @@ -92,6 +92,8 @@ extern func mu_init(ctx: *mu_Context) void; extern func mu_begin(ctx: *mu_Context) void; extern func mu_end(ctx: *mu_Context) void; +extern func mu_layout_next(ctx: *mu_Context) mu_Rect; + func mu_button(ctx: *mu_Context, label: *char) sint { return mu_button_ex(ctx, label, 0, (:sint)MU_OPT_ALIGNCENTER); } func mu_textbox(ctx: *mu_Context, buf: *char, bufsz: sint) sint { return mu_textbox_ex(ctx, buf, bufsz, 0); } func mu_slider(ctx: *mu_Context, value: *mu_Real, lo: mu_Real, hi: mu_Real) sint { return mu_slider_ex(ctx, value, lo, hi, 0.0, MU_SLIDER_FMT, (:sint)MU_OPT_ALIGNCENTER); } @@ -101,7 +103,7 @@ func mu_begin_treenode(ctx: *mu_Context, label: *char) sint { return mu_begin_tr func mu_begin_window(ctx: *mu_Context, title: *char, rect: mu_Rect) sint { return mu_begin_window_ex(ctx, title, rect, 0); } func mu_begin_panel(ctx: *mu_Context, name: *char) void { mu_begin_panel_ex(ctx, name, 0); } -extern func mu_text(ctx: *mu_Context, text: *byte) void; +extern func mu_text(ctx: *mu_Context, text: *char) void; extern func mu_label(ctx: *mu_Context, text: *char) void; extern func mu_button_ex(ctx: *mu_Context, label: *char, icon: sint, opt: sint) sint; extern func mu_checkbox(ctx: *mu_Context, label: *char, state: *sint) sint; diff --git a/smolui.c b/smolui.c index 8635b23..e75c5ba 100644 --- a/smolui.c +++ b/smolui.c @@ -44,7 +44,7 @@ smolui_mu_context_del(mu_Context* ctx) } void -smolui_setup_font_ex(mu_Context* ctx, Font const* font) +smolui_setup_font(mu_Context* ctx, Font const* font) { ctx->style->font = (mu_Font)font; ctx->text_width = smolui_text_width; diff --git a/smolui.h b/smolui.h index 08ce5f4..129e003 100644 --- a/smolui.h +++ b/smolui.h @@ -31,8 +31,8 @@ void smolui_mu_context_del(mu_Context* ctx); #define SMOLUI_VECTOR2_FROM_MU(v) ((Vector2){v.x, v.y}) // Set the text height/width callbacks and the font. -void smolui_setup_font_ex(mu_Context* ctx, Font const* font); -#define smolui_setup_font(ctx) smolui_setup_font_ex(ctx, NULL) +// Providing a NULL font pointer wll use the default raylib font. +void smolui_setup_font(mu_Context* ctx, Font const* font); // `mu_Context.text_width` callback. See `smolui_setup_font`. int smolui_text_width(mu_Font font, char const* str, int len); diff --git a/smolui.sunder b/smolui.sunder index a11c92b..8278291 100644 --- a/smolui.sunder +++ b/smolui.sunder @@ -1,4 +1,6 @@ namespace smolui; +import "c"; +import "std"; import "raylib"; @@ -7,6 +9,199 @@ import "microui.sunder"; extern func mu_context_new() *mu_Context; extern func mu_context_del(ctx: *mu_Context) void; -extern func setup_font_ex(ctx: *mu_Context, font: *Font) void; +extern func setup_font(ctx: *mu_Context, font: *Font) void; extern func handle_input(ctx: *mu_Context) void; extern func render(ctx: *mu_Context) void; + +alias rect = mu_Rect; + +var RES_ACTIVE = (:sint)MU_RES_ACTIVE; +var RES_SUBMIT = (:sint)MU_RES_SUBMIT; +var RES_CHANGE = (:sint)MU_RES_CHANGE; + +var ICON_CLOSE = (:sint)MU_ICON_CLOSE; +var ICON_CHECK = (:sint)MU_ICON_CHECK; +var ICON_COLLAPSED = (:sint)MU_ICON_COLLAPSED; +var ICON_EXPANDED = (:sint)MU_ICON_EXPANDED; +var ICON_MAX = (:sint)MU_ICON_MAX; + +var OPT_ALIGNCENTER = (:sint)MU_OPT_ALIGNCENTER; +var OPT_ALIGNRIGHT = (:sint)MU_OPT_ALIGNRIGHT; +var OPT_NOINTERACT = (:sint)MU_OPT_NOINTERACT; +var OPT_NOFRAME = (:sint)MU_OPT_NOFRAME; +var OPT_NORESIZE = (:sint)MU_OPT_NORESIZE; +var OPT_NOSCROLL = (:sint)MU_OPT_NOSCROLL; +var OPT_NOCLOSE = (:sint)MU_OPT_NOCLOSE; +var OPT_NOTITLE = (:sint)MU_OPT_NOTITLE; +var OPT_HOLDFOCUS = (:sint)MU_OPT_HOLDFOCUS; +var OPT_AUTOSIZE = (:sint)MU_OPT_AUTOSIZE; +var OPT_POPUP = (:sint)MU_OPT_POPUP; +var OPT_CLOSED = (:sint)MU_OPT_CLOSED; +var OPT_EXPANDED = (:sint)MU_OPT_EXPANDED; + +struct context { + var mu_ctx: *mu_Context; + + # Init a UI context with provided font. + # Providing a NULL font pointer wll use the default raylib font. + func init(font: *Font) context { + var ctx = mu_context_new(); + mu_init(ctx); + setup_font(ctx, font); + return (:context){.mu_ctx = ctx}; + } + + func fini(self: *context) void { + mu_context_del(self.*.mu_ctx); + } + + func handle_input(self: *context) void { + smolui::handle_input(self.*.mu_ctx); + } + + func begin(self: *context) void { + mu_begin(self.*.mu_ctx); + } + + func end(self: *context) void { + mu_end(self.*.mu_ctx); + } + + func text(self: *context, text: []byte) void { + var s = std::string::init_from_str(text); + defer s.fini(); + mu_text(self.*.mu_ctx, s.cstr()); + } + + func label(self: *context, text: []byte) void { + var s = std::string::init_from_str(text); + defer s.fini(); + mu_label(self.*.mu_ctx, s.cstr()); + } + + func button(self: *context, text: []byte) bool { + var s = std::string::init_from_str(text); + defer s.fini(); + return mu_button(self.*.mu_ctx, s.cstr()) != 0; + } + + func button_ex(self: *context, text: []byte, icon: sint, opt: sint) bool { + var s = std::string::init_from_str(text); + defer s.fini(); + return mu_button_ex(self.*.mu_ctx, s.cstr(), icon, opt) != 0; + } + + func checkbox(self: *context, text: []byte, state: *bool) bool { + var s = std::string::init_from_str(text); + defer s.fini(); + var int = (:sint)*state; + var res = mu_checkbox(self.*.mu_ctx, s.cstr(), &int) != 0; + *state = (:bool)int; + return res; + } + + func textbox(self: *context, text: *std::string) bool { + return self.*.textbox_ex(text, 0) != 0; + } + + func textbox_ex(self: *context, text: *std::string, opt: sint) sint { + # The address of the buffer may change during resize, so we use the + # address of the std::string as the ID rather than the hashed value of + # the buffer address that mu_textbox_ex would normally use. + var id = (:mu_Id)(:usize)text; + var r = mu_layout_next(self.*.mu_ctx); + let INPUT_TEXT_SIZE = 32u; + text.*.reserve(text.*.count() + INPUT_TEXT_SIZE); + var res = mu_textbox_raw(self.*.mu_ctx, text.*.start(), (:sint)(text.*.capacity() + countof("\0")), id, r, opt); + text.*.resize(std::cstr::count(text.*.cstr())); + return res; + } + + func slider(self: *context, value: *float, lo: float, hi: float) bool { + return mu_slider(self.*.mu_ctx, value, lo, hi) != 0; + } + + func number(self: *context, value: *float, step: float) bool { + return mu_number(self.*.mu_ctx, value, step) != 0; + } + + func header(self: *context, text: []byte) bool { + var s = std::string::init_from_str(text); + defer s.fini(); + return mu_header(self.*.mu_ctx, s.cstr()) != 0; + } + + func header_ex(self: *context, text: []byte, opt: sint) bool { + var s = std::string::init_from_str(text); + defer s.fini(); + return mu_header_ex(self.*.mu_ctx, s.cstr(), opt) != 0; + } + + func begin_treenode(self: *context, text: []byte) bool { + var s = std::string::init_from_str(text); + defer s.fini(); + return mu_begin_treenode(self.*.mu_ctx, s.cstr()) != 0; + } + + func begin_treenode_ex(self: *context, text: []byte, opt: sint) bool { + var s = std::string::init_from_str(text); + defer s.fini(); + return mu_begin_treenode_ex(self.*.mu_ctx, s.cstr(), opt) != 0; + } + + func end_treenode(self: *context) void { + mu_end_treenode(self.*.mu_ctx); + } + + func begin_window(self: *context, title: []byte, rect: rect) bool { + var s = std::string::init_from_str(title); + defer s.fini(); + return mu_begin_window(self.*.mu_ctx, s.cstr(), rect) != 0; + } + + func begin_window_ex(self: *context, title: []byte, rect: rect, opt: sint) bool { + var s = std::string::init_from_str(title); + defer s.fini(); + return mu_begin_window_ex(self.*.mu_ctx, s.cstr(), rect, opt) != 0; + } + + func end_window(self: *context) void { + mu_end_window(self.*.mu_ctx); + } + + func open_popup(self: *context, name: []byte) void { + var s = std::string::init_from_str(name); + defer s.fini(); + mu_open_popup(self.*.mu_ctx, s.cstr()); + } + + func begin_popup(self: *context, name: []byte) bool { + var s = std::string::init_from_str(name); + defer s.fini(); + return mu_begin_popup(self.*.mu_ctx, s.cstr()) != 0; + } + + func end_popup(self: *context) void { + mu_end_popup(self.*.mu_ctx); + } + + func begin_panel(self: *context, name: []byte) void { + var s = std::string::init_from_str(name); + defer s.fini(); + mu_begin_panel(self.*.mu_ctx, s.cstr()); + } + + func begin_panel_ex(self: *context, name: []byte, opt: sint) void { + var s = std::string::init_from_str(name); + defer s.fini(); + mu_begin_panel_ex(self.*.mu_ctx, s.cstr(), opt); + } + + func end_panel(self: *context) void { + mu_end_panel(self.*.mu_ctx); + } + + func render(self: *context) void { + smolui::render(self.*.mu_ctx); + } +}