--- a/ui/motif/container.c Mon Jan 06 22:22:55 2025 +0100 +++ b/ui/motif/container.c Tue Feb 25 21:11:00 2025 +0100 @@ -34,8 +34,26 @@ #include "../common/context.h" #include "../common/object.h" +#include <cx/array_list.h> + #include "Grid.h" + +UIWIDGET ui_customwidget_create(UiObject *obj, ui_createwidget_func create_widget, void *userdata, UiWidgetArgs args) { + Arg xargs[64]; + int n = 0; + + UiContainerPrivate *ctn = ui_obj_container(obj); + UI_APPLY_LAYOUT(ctn->layout, args); + + Widget parent = ctn->prepare(ctn, xargs, &n); + Widget widget = create_widget(obj, args, userdata, parent, xargs, n); + XtManageChild(widget); + ctn->add(ctn, widget); + + return widget; +} + /* ---------------------------- Box Container ---------------------------- */ static UIWIDGET box_create(UiObject *obj, UiContainerArgs args, UiBoxOrientation orientation) { @@ -203,6 +221,344 @@ } +/* -------------------------- TabView Container -------------------------- */ + +static void ui_tabbar_resize(Widget widget, XtPointer udata, XtPointer cdata) { + UiMotifTabView *tabview = udata; + + if(tabview->tabview == UI_TABVIEW_INVISIBLE) { + return; + } + + int width = 0; + int height = 0; + XtVaGetValues(widget, XmNwidth, &width, XmNheight, &height, NULL); + int numbuttons = cxListSize(tabview->tabs); + if(numbuttons == 0) { + return; + } + int button_width = width / numbuttons; + int x = 0; + + CxIterator i = cxListIterator(tabview->tabs); + cx_foreach(UiTab *, tab, i) { + if(i.index + 1 == numbuttons) { + button_width = width - x; + } + XtVaSetValues( + tab->tab_button, + XmNx, x, + XmNy, 0, + XmNwidth, + button_width, + + NULL); + x += button_width; + } + + if(height <= tabview->height) { + XtVaSetValues(widget, XmNheight, tabview->height + 4, NULL); + } +} + +static void ui_tabbar_expose(Widget widget, XtPointer udata, XtPointer cdata) { + UiMotifTabView *tabview = udata; + XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct *)cdata; + XEvent *event = cbs->event; + Display *dpy = XtDisplay(widget); + + if(!tabview->gc_initialized) { + XGCValues gcvals; + gcvals.foreground = tabview->fg1; + tabview->gc = XCreateGC(XtDisplay(tabview->tabbar), XtWindow(tabview->tabbar), (GCForeground), &gcvals); + } + + if(tabview->current_tab) { + Widget tab = tabview->current_tab->tab_button; + XFillRectangle(dpy, XtWindow(widget), tabview->gc, tab->core.x, tab->core.height, tab->core.width, 4); + } +} + +UIWIDGET ui_tabview_create(UiObject *obj, UiTabViewArgs args) { + Arg xargs[16]; + int n = 0; + + UiContainerPrivate *ctn = ui_obj_container(obj); + UI_APPLY_LAYOUT(ctn->layout, args); + + // create widgets + // form + // - tabbar (Drawing Area) + // - content (Frame) + UiMotifTabView *tabview = malloc(sizeof(UiMotifTabView)); + memset(tabview, 0, sizeof(UiMotifTabView)); + char *name = args.name ? (char*)args.name : "tabview"; + XtSetArg(xargs[n], XmNuserData, tabview); n++; + Widget parent = ctn->prepare(ctn, xargs, &n); + Widget form = XmCreateForm(parent, name, xargs, n); + XtManageChild(form); + + n = 0; + XtSetArg(xargs[n], XmNleftAttachment, XmATTACH_FORM); n++; + XtSetArg(xargs[n], XmNrightAttachment, XmATTACH_FORM); n++; + XtSetArg(xargs[n], XmNtopAttachment, XmATTACH_FORM); n++; + XtSetArg(xargs[n], XmNorientation, XmHORIZONTAL); n++; + XtSetArg(xargs[n], XmNpacking, XmPACK_TIGHT); n++; + XtSetArg(xargs[n], XmNspacing, 1); n++; + XtSetArg(xargs[n], XmNmarginWidth, 0); n++; + XtSetArg(xargs[n], XmNmarginHeight, 0); n++; + Widget tabbar = XmCreateDrawingArea(form, "ui_test", xargs, n); + XtManageChild(tabbar); + XtAddCallback(tabbar, XmNresizeCallback , ui_tabbar_resize, tabview); + XtAddCallback(tabbar, XmNexposeCallback, ui_tabbar_expose, tabview); + + n = 0; + XtSetArg(xargs[n], XmNleftAttachment, XmATTACH_FORM); n++; + XtSetArg(xargs[n], XmNrightAttachment, XmATTACH_FORM); n++; + XtSetArg(xargs[n], XmNbottomAttachment, XmATTACH_FORM); n++; + XtSetArg(xargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++; + XtSetArg(xargs[n], XmNtopWidget, tabbar); n++; + Widget content = XmCreateFrame(form, "tabviewcontent", xargs, n); + + // setup tabview object, that holds all relevant objects + tabview->obj = obj; + tabview->form = form; + tabview->tabbar = tabbar; + tabview->content = content; + tabview->tabview = args.tabview; + tabview->subcontainer = args.subcontainer; + tabview->select = ui_motif_tabview_select; + tabview->add = ui_motif_tabview_add_tab; + tabview->remove = ui_motif_tabview_remove; + tabview->tabs = cxArrayListCreate(obj->ctx->allocator, cx_cmp_ptr, sizeof(UiTab), 8); + tabview->current_index = -1; + + UiTabViewContainer *ct = ui_malloc(obj->ctx, sizeof(UiTabViewContainer)); + ct->container.widget = form; + ct->container.type = UI_CONTAINER_TABVIEW; + ct->container.prepare = ui_tabview_container_prepare; + ct->container.add = ui_tabview_container_add; + ct->tabview = tabview; + + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args.value, args.varname, UI_VAR_INTEGER); + if(var) { + UiInteger *i = var->value; + i->obj = tabview; + i->get = ui_tabview_get; + i->set = ui_tabview_set; + } + + uic_object_push_container(obj, (UiContainerX*)ct); + + return form; +} + +int64_t ui_tabview_get(UiInteger *i) { + UiMotifTabView *tabview = i->obj; + i->value = tabview->current_index; + return i->value; +} + +void ui_tabview_set(UiInteger *i, int64_t value) { + UiMotifTabView *tabview = i->obj; + if(value < cxListSize(tabview->tabs)) { + ui_motif_tabview_select(tabview, value); + i->value = value; + } +} + +void ui_tab_create(UiObject *obj, const char* title) { + UiContainerPrivate *ctn = ui_obj_container(obj); + if(ctn->type != UI_CONTAINER_TABVIEW) { + fprintf(stderr, "UI Error: container is not a tabview\n"); + return; + } + + UiMotifTabView *tabview = NULL; + XtVaGetValues(ctn->widget, XmNuserData, &tabview, NULL); + if(!tabview) { + fprintf(stderr, "UI Error: no tabview\n"); + return; + } + + + Widget child = ui_vbox_create(obj, (UiContainerArgs) { 0 }); + if(tabview->current) { + XtUnmanageChild(child); + } else { + tabview->current = child; + } + + tabview->add(tabview, -1, title, child); +} + +void ui_tabview_select(UIWIDGET tabview, int tab) { + UiMotifTabView *tabviewdata = NULL; + XtVaGetValues(tabview, XmNuserData, &tabviewdata, NULL); + if(tabviewdata) { + ui_motif_tabview_select(tabviewdata, tab); + } else { + fprintf(stderr, "ui_tabview_select: widget is not a tabview\n"); + } +} + +void ui_tabview_remove(UIWIDGET tabview, int tab) { + UiMotifTabView *tabviewdata = NULL; + XtVaGetValues(tabview, XmNuserData, &tabviewdata, NULL); + if(tabviewdata) { + ui_motif_tabview_remove(tabviewdata, tab); + } else { + fprintf(stderr, "ui_tabview_select: widget is not a tabview\n"); + } +} + +UiObject* ui_tabview_add(UIWIDGET tabview, const char *name, int tab_index) { + UiMotifTabView *tabviewdata = NULL; + XtVaGetValues(tabview, XmNuserData, &tabviewdata, NULL); + if(tabviewdata) { + Arg args[16]; + int n = 0; + + XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; + XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; + XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; + XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; + XtSetArg(args[n], XmNtopWidget, tabviewdata->tabbar); n++; + + Widget grid = XtCreateManagedWidget("vbox", gridClass, tabviewdata->content, args, n); + + UiObject *newobj = ui_calloc(tabviewdata->obj->ctx, 1, sizeof(UiObject)); + newobj->ctx = tabviewdata->obj->ctx; + newobj->widget = grid; + UiContainerX *container = ui_box_container(newobj, grid, UI_BOX_VERTICAL); + newobj->container_begin = container; + newobj->container_end = container; + return newobj; + } else { + fprintf(stderr, "ui_tabview_select: widget is not a tabview\n"); + return NULL; + } +} + +void ui_motif_tabview_select(UiMotifTabView *tabview, int tab) { + UiTab *t = cxListAt(tabview->tabs, tab); + if(t) { + tabview->current_index = tab; + ui_motif_tabview_change_tab(tabview, t); + } +} + +static void ui_tab_button_callback(Widget widget, UiTab *tab, XtPointer d) { + UiMotifTabView *tabview = NULL; + XtVaGetValues(widget, XmNuserData, &tabview, NULL); + ui_motif_tabview_change_tab(tabview, tab); +} + +void ui_motif_tabview_add_tab(UiMotifTabView *tabview, int index, const char *name, Widget child) { + UiTab tab; + + Arg args[16]; + int n = 0; + + XmString label = XmStringCreateLocalized((char*)name); + XtSetArg(args[n], XmNlabelString, label); n++; + XtSetArg(args[n], XmNshadowThickness, 0); n++; + XtSetArg(args[n], XmNhighlightThickness, 0); n++; + XtSetArg(args[n], XmNuserData, tabview); n++; + + Widget button = XmCreatePushButton(tabview->tabbar, "tab_button", args, n); + if(tabview->tabview != UI_TABVIEW_INVISIBLE) { + XtManageChild(button); + } + + if(tabview->height == 0) { + Dimension h; + XtVaGetValues( + button, + XmNarmColor, + &tabview->bg1, + XmNbackground, + &tabview->bg2, + XmNhighlightColor, + &tabview->fg1, + XmNheight, + &h, + NULL); + tabview->height = h + 2; // border + + XtVaSetValues(tabview->tabbar, XmNbackground, tabview->bg1, NULL); + } + + tab.tab_button = button; + tab.child = child; + size_t newtab_index = cxListSize(tabview->tabs); + cxListAdd(tabview->tabs, &tab); + UiTab *newtab = cxListAt(tabview->tabs, newtab_index); + + XtAddCallback( + button, + XmNactivateCallback, + (XtCallbackProc)ui_tab_button_callback, + newtab); + + if(newtab_index == 0) { + ui_motif_tabview_change_tab(tabview, newtab); + } else { + XtVaSetValues(button, XmNbackground, tabview->bg1, NULL); + } +} + +void ui_motif_tabview_remove(UiMotifTabView *tabview, int index) { + UiTab *tab = cxListAt(tabview->tabs, index); + if(tab) { + if(tab == tabview->current_tab) { + if(index > 0) { + ui_motif_tabview_select(tabview, index-1); + } else { + if(index < cxListSize(tabview->tabs)) { + ui_motif_tabview_select(tabview, index+1); + } else { + tabview->current_tab = NULL; + tabview->current_index = -1; + } + } + } + XtDestroyWidget(tab->tab_button); + XtDestroyWidget(tab->child); + cxListRemove(tabview->tabs, index); + } +} + +void ui_motif_tabview_change_tab(UiMotifTabView *tabview, UiTab *tab) { + if(tabview->current_tab) { + XtVaSetValues(tabview->current_tab->tab_button, XmNshadowThickness, 0, XmNbackground, tabview->bg1, NULL); + XtUnmanageChild(tabview->current_tab->child); + } + XtVaSetValues(tab->tab_button, XmNshadowThickness, 1, XmNbackground, tabview->bg2, NULL); + tabview->current_tab = tab; + tabview->current_index = (int)cxListFind(tabview->tabs, tab);; + XtManageChild(tab->child); +} + +Widget ui_tabview_container_prepare(UiContainerPrivate *ctn, Arg *args, int *n) { + UiTabViewContainer *ct = (UiTabViewContainer*)ctn; + UiMotifTabView *tabview = ct->tabview; + int a = *n; + XtSetArg(args[a], XmNleftAttachment, XmATTACH_FORM); a++; + XtSetArg(args[a], XmNrightAttachment, XmATTACH_FORM); a++; + XtSetArg(args[a], XmNbottomAttachment, XmATTACH_FORM); a++; + XtSetArg(args[a], XmNtopAttachment, XmATTACH_WIDGET); a++; + XtSetArg(args[a], XmNtopWidget, tabview->tabbar); a++; + *n = a; + return tabview->form; +} + +void ui_tabview_container_add(UiContainerPrivate *ctn, Widget widget) { + ui_reset_layout(ctn->layout); +} + + + /* -------------------- Container Helper Functions -------------------- */ void ui_container_begin_close(UiObject *obj) {