nappgui_src
nappgui_src copied to clipboard
Dynamically unloading GUI elements
Hello,
I'm working on a application that requires some dynamic UI behavior. In the context of this application this means adding and removing (not hiding but outright destroying) arbitrary GUI elements to a existing (and visible) window.
Looking at the documentation and going through the examples I haven't found a way to achieve this.
Is this something that is doable?
Thank you!
Hi @kicsyromy! Yes you can do it with panels. The layout_panel()
function "cleanly" destroys the previously assigned panel. The rest of the elements of a layout (buttons, labels, slider, view, etc) cannot be changed dynamically. Here a demo (source + exe). I will add same similiar example to documentation.
When you close the App, you can see that there is no MemoryLeaks
[16:42:39] [OK] Heap Memory Staticstics
[16:42:39] ============================
[16:42:39] Total a/dellocations: 654, 654
[16:42:39] Total bytes a/dellocated: 72366, 72366
[16:42:39] Max bytes allocated: 26198
[16:42:39] Effective reallocations: (0/30)
[16:42:39] Real allocations: 2 pages of 65536 bytes
[16:42:39] ============================
[16:42:39] Config: Debug
/* NAppGUI Hello World */
#include "nappgui.h"
typedef struct _app_t App;
struct _app_t
{
Window *window;
Layout *layout;
uint32_t panel_id;
};
/*---------------------------------------------------------------------------*/
// Dynamic Panel 0 --> Just a label
static Panel *i_panel0(void)
{
Panel *panel = panel_create();
Layout *layout = layout_create(1, 1);
Label *label = label_create();
label_text(label, "This is a very small panel");
layout_label(layout, label, 0, 0);
panel_layout(panel, layout);
return panel;
}
/*---------------------------------------------------------------------------*/
// Dynamic Panel 1 --> 'n' pairs Label:Slider
static Panel *i_panel1(void)
{
uint32_t i, n = 5;
Panel *panel = panel_create();
Layout *layout = layout_create(2, 5);
for (i = 0; i < n; ++i)
{
Label *label = label_create();
Slider *slider = slider_create();
char_t text[128];
bstd_sprintf(text, sizeof(text), "Dynamic variable %d", i + 1);
label_text(label, text);
layout_label(layout, label, 0, i);
layout_slider(layout, slider, 1, i);
layout_hsize(layout, 1, 150);
if (i < n - 1)
layout_vmargin(layout, i, 5);
}
layout_hmargin(layout, 0, 5);
panel_layout(panel, layout);
return panel;
}
/*---------------------------------------------------------------------------*/
static void i_OnDraw1(App *app, Event *e)
{
const EvDraw *p = event_params(e, EvDraw);
draw_clear(p->ctx, kCOLOR_RED);
unref(app);
}
/*---------------------------------------------------------------------------*/
static void i_OnDraw2(App *app, Event *e)
{
const EvDraw *p = event_params(e, EvDraw);
draw_clear(p->ctx, kCOLOR_BLUE);
unref(app);
}
/*---------------------------------------------------------------------------*/
// Dynamic Panel 2 --> Two dynamic drawings
static Panel *i_panel2(App *app)
{
Panel *panel = panel_create();
Layout *layout = layout_create(2, 1);
View *view1 = view_create();
View *view2 = view_create();
view_OnDraw(view1, listener(app, i_OnDraw1, App));
view_OnDraw(view2, listener(app, i_OnDraw2, App));
view_size(view1, s2df(256, 256));
view_size(view2, s2df(256, 256));
layout_view(layout, view1, 0, 0);
layout_view(layout, view2, 1, 0);
layout_hmargin(layout, 0, 5);
panel_layout(panel, layout);
return panel;
}
/*---------------------------------------------------------------------------*/
static Panel *i_dynamic_panel(App *app, const uint32_t id)
{
switch (id) {
case 0:
return i_panel0();
case 1:
return i_panel1();
case 2:
return i_panel2(app);
cassert_default();
}
return NULL;
}
/*---------------------------------------------------------------------------*/
static void i_OnRadio(App *app, Event *e)
{
const EvButton *p = event_params(e, EvButton);
cassert_no_null(app);
if (app->panel_id != p->index)
{
Panel *panel = i_dynamic_panel(app, p->index);
//
// Layout panel will correctly change the current panel
// with the new panel. Current panel will be correctly destroyed.
//
layout_panel(app->layout, panel, 0, 1);
//
// Force the current window to recompute/resize
//
layout_update(app->layout);
app->panel_id = p->index;
}
}
/*---------------------------------------------------------------------------*/
static Layout *i_layout(App *app)
{
Layout *layout1 = layout_create(1, 2);
Layout *layout2 = layout_create(3, 1);
Button *radio0 = button_radio();
Button *radio1 = button_radio();
Button *radio2 = button_radio();
Panel *panel = i_dynamic_panel(app, app->panel_id);
button_text(radio0, "Panel 0");
button_text(radio1, "Panel 1");
button_text(radio2, "Panel 2");
button_state(radio0, app->panel_id == 0 ? ekON : ekOFF);
button_state(radio1, app->panel_id == 1 ? ekON : ekOFF);
button_state(radio2, app->panel_id == 2 ? ekON : ekOFF);
button_OnClick(radio0, listener(app, i_OnRadio, App));
layout_hmargin(layout2, 0, 5);
layout_hmargin(layout2, 1, 5);
layout_vmargin(layout1, 0, 10);
layout_margin(layout1, 10);
layout_halign(layout1, 0, 0, ekLEFT);
layout_button(layout2, radio0, 0, 0);
layout_button(layout2, radio1, 1, 0);
layout_button(layout2, radio2, 2, 0);
layout_layout(layout1, layout2, 0, 0);
layout_panel(layout1, panel, 0, 1);
return layout1;
}
/*---------------------------------------------------------------------------*/
static Panel *i_panel(App *app)
{
Panel *panel = panel_create();
Layout *layout = i_layout(app);
panel_layout(panel, layout);
//
// The keep the main layout for futher editions in 'i_OnRadio'
//
app->layout = layout;
return panel;
}
/*---------------------------------------------------------------------------*/
static void i_OnClose(App *app, Event *e)
{
osapp_finish();
unref(app);
unref(e);
}
/*---------------------------------------------------------------------------*/
static App *i_create(void)
{
App *app = heap_new0(App);
Panel *panel = i_panel(app);
app->window = window_create(ekWNSTD);
window_panel(app->window, panel);
window_title(app->window, "Dynamic Panels");
window_origin(app->window, v2df(500, 200));
window_OnClose(app->window, listener(app, i_OnClose, App));
window_show(app->window);
return app;
}
/*---------------------------------------------------------------------------*/
static void i_destroy(App **app)
{
window_destroy(&(*app)->window);
heap_delete(app, App);
}
/*---------------------------------------------------------------------------*/
#include "osmain.h"
osmain(i_create, i_destroy, "", App)
Hi @frang75,
This is amazing! Thank you so much for the very detailed response and example.
From my POV the issue can be closed after a documentation update.
Keep up the great work! 🙂
Related with https://github.com/frang75/nappgui_src/issues/74
WIP
This feature is officially supported in this commit: https://github.com/frang75/nappgui_src/commit/1b6d902b8b3e19ddb185154ae3488a5c6637b02b
Using this new function: layout_panel_replace()
Documentation: https://nappgui.com/en/gui/layout.html#h6.1