--- a/ui/motif/text.c Sun Dec 07 20:00:33 2025 +0100 +++ b/ui/motif/text.c Sat Dec 13 15:58:58 2025 +0100 @@ -32,6 +32,9 @@ #include "text.h" #include "container.h" +#include "pathbar.h" + +#include "../common/utils.h" #include <cx/string.h> @@ -210,9 +213,9 @@ int sel = left < right ? 1 : 0; if(sel != textarea->last_selection_state) { if(sel) { - ui_set_group(textarea->obj->ctx, UI_GROUP_SELECTION); + ui_set_state(textarea->obj->ctx, UI_GROUP_SELECTION); } else { - ui_unset_group(textarea->obj->ctx, UI_GROUP_SELECTION); + ui_unset_state(textarea->obj->ctx, UI_GROUP_SELECTION); } } textarea->last_selection_state = sel; @@ -405,10 +408,20 @@ XtManageChild(textfield); ui_container_add(ctn, textfield); - ui_set_widget_groups(obj->ctx, textfield, args->groups); + ui_set_widget_groups(obj->ctx, textfield, args->states); + + UiEventDataExt *eventdata = malloc(sizeof(UiEventDataExt)); + memset(eventdata, 0, sizeof(UiEventDataExt)); + eventdata->obj = obj; + eventdata->callback = args->onactivate; + eventdata->userdata = args->onactivatedata; + eventdata->callback2 = args->onchange; + eventdata->userdata2 = args->onchangedata; UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING); if(var) { + eventdata->customdata0 = var; + UiString *value = (UiString*)var->value; value->obj = textfield; value->get = ui_textfield_get; @@ -419,9 +432,54 @@ } } + XtAddCallback( + textfield, + XmNactivateCallback, + (XtCallbackProc)ui_textfield_activate, + eventdata); + XtAddCallback( + textfield, + XmNvalueChangedCallback, + (XtCallbackProc)ui_textfield_value_changed, + eventdata); + XtAddCallback( + textfield, + XmNdestroyCallback, + (XtCallbackProc)ui_destroy_data, + eventdata); + return textfield; } +static void textfield_event(UiEventDataExt *eventdata, ui_callback callback, void *userdata) { + if(callback) { + UiVar *var = eventdata->customdata0; + UiString *value = var ? var->value : NULL; + + UiEvent e; + e.obj = eventdata->obj; + e.window = e.obj->window; + e.document = e.obj->ctx->document; + e.eventdata = value; + e.eventdatatype = value ? UI_EVENT_DATA_TEXT_VALUE : 0; + e.intval = 0; + e.set = ui_get_setop(); + callback(&e, userdata); + } +} + +void ui_textfield_activate(Widget widget, XtPointer ud, XtPointer cb) { + UiEventDataExt *eventdata = ud; + textfield_event(ud, eventdata->callback, eventdata->userdata); +} + +void ui_textfield_value_changed(Widget widget, XtPointer ud, XtPointer cb) { + UiEventDataExt *eventdata = ud; + if(ui_onchange_events_is_enabled()) { + textfield_event(ud, eventdata->callback2, eventdata->userdata2); + } +} + UIWIDGET ui_textfield_create(UiObject *obj, UiTextFieldArgs *args) { return create_textfield(obj, args, FALSE, FALSE); } @@ -453,459 +511,6 @@ } - - - -/* -------------------- path bar -------------------- */ - -#define XNECreateText(parent,name,args,count) XmCreateTextField(parent,name,args,count) -#define XNETextSetString(widget,value) XmTextFieldSetString(widget,value) -#define XNETextGetString(widget) XmTextFieldGetString(widget) -#define XNETextGetLastPosition(widget) XmTextFieldGetLastPosition(widget) -#define XNETextSetInsertionPosition(widget, i) XmTextFieldSetInsertionPosition(widget, i) -#define XNETextSetSelection(w, f, l, t) XmTextFieldSetSelection(w, f, l, t) - -typedef void(*updatedir_callback)(void*,char*,int); - -typedef struct PathBar { - Widget widget; - Widget textfield; - - Widget focus_widget; - - Widget left; - Widget right; - Dimension lw; - Dimension rw; - - int shift; - - UiPathElm *current_pathelms; - Widget *pathSegments; - size_t numSegments; - size_t segmentAlloc; - - char *path; - int selection; - Boolean input; - - int focus; - - updatedir_callback updateDir; - void *updateDirData; - - ui_pathelm_func getpathelm; - void *getpathelmdata; -} PathBar; - -void PathBarSetPath(PathBar *bar, const char *path); - -void pathbar_resize(Widget w, PathBar *p, XtPointer d) -{ - Dimension width, height; - XtVaGetValues(w, XmNwidth, &width, XmNheight, &height, NULL); - - Dimension *segW = (void*)XtCalloc(p->numSegments, sizeof(Dimension)); - - Dimension maxHeight = 0; - - /* get width/height from all widgets */ - Dimension pathWidth = 0; - for(int i=0;i<p->numSegments;i++) { - Dimension segWidth; - Dimension segHeight; - XtVaGetValues(p->pathSegments[i], XmNwidth, &segWidth, XmNheight, &segHeight, NULL); - segW[i] = segWidth; - pathWidth += segWidth; - if(segHeight > maxHeight) { - maxHeight = segHeight; - } - } - Dimension tfHeight; - XtVaGetValues(p->textfield, XmNheight, &tfHeight, NULL); - if(tfHeight > maxHeight) { - maxHeight = tfHeight; - } - - Boolean arrows = False; - if(pathWidth + 10 > width) { - arrows = True; - pathWidth += p->lw + p->rw; - } - - /* calc max visible widgets */ - int start = 0; - if(arrows) { - Dimension vis = p->lw+p->rw; - for(int i=p->numSegments;i>0;i--) { - Dimension segWidth = segW[i-1]; - if(vis + segWidth + 10 > width) { - start = i; - arrows = True; - break; - } - vis += segWidth; - } - } else { - p->shift = 0; - } - - int leftShift = 0; - if(p->shift < 0) { - if(start + p->shift < 0) { - leftShift = start; - start = 0; - p->shift = -leftShift; - } else { - leftShift = -p->shift; /* negative shift */ - start += p->shift; - } - } - - int x = 0; - if(arrows) { - XtManageChild(p->left); - XtManageChild(p->right); - x = p->lw; - } else { - XtUnmanageChild(p->left); - XtUnmanageChild(p->right); - } - - for(int i=0;i<p->numSegments;i++) { - if(i >= start && i < p->numSegments - leftShift && !p->input) { - XtVaSetValues(p->pathSegments[i], XmNx, x, XmNy, 0, XmNheight, maxHeight, NULL); - x += segW[i]; - XtManageChild(p->pathSegments[i]); - } else { - XtUnmanageChild(p->pathSegments[i]); - } - } - - if(arrows) { - XtVaSetValues(p->left, XmNx, 0, XmNy, 0, XmNheight, maxHeight, NULL); - XtVaSetValues(p->right, XmNx, x, XmNy, 0, XmNheight, maxHeight, NULL); - } - - free(segW); - - Dimension rw, rh; - XtMakeResizeRequest(w, width, maxHeight, &rw, &rh); - - XtVaSetValues(p->textfield, XmNwidth, rw, XmNheight, rh, NULL); -} - -static void pathbarActivateTF(PathBar *p) -{ - XtUnmanageChild(p->left); - XtUnmanageChild(p->right); - XNETextSetSelection(p->textfield, 0, XNETextGetLastPosition(p->textfield), 0); - XtManageChild(p->textfield); - p->input = 1; - - XmProcessTraversal(p->textfield, XmTRAVERSE_CURRENT); - - pathbar_resize(p->widget, p, NULL); -} - -void PathBarActivateTextfield(PathBar *p) -{ - p->focus = 1; - pathbarActivateTF(p); -} - -void pathbar_input(Widget w, PathBar *p, XtPointer c) -{ - XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct*)c; - XEvent *xevent = cbs->event; - - if (cbs->reason == XmCR_INPUT) { - if (xevent->xany.type == ButtonPress) { - p->focus = 0; - pathbarActivateTF(p); - } - } -} - -void pathbar_losingfocus(Widget w, PathBar *p, XtPointer c) -{ - if(--p->focus < 0) { - p->input = False; - XtUnmanageChild(p->textfield); - } -} - -static cxmutstr concat_path_s(cxstring base, cxstring path) { - if(!path.ptr) { - path = CX_STR(""); - } - - int add_separator = 0; - if(base.length != 0 && base.ptr[base.length-1] == '/') { - if(path.ptr[0] == '/') { - base.length--; - } - } else { - if(path.length == 0 || path.ptr[0] != '/') { - add_separator = 1; - } - } - - cxmutstr url; - if(add_separator) { - url = cx_strcat(3, base, CX_STR("/"), path); - } else { - url = cx_strcat(2, base, path); - } - - return url; -} - -static char* ConcatPath(const char *path1, const char *path2) { - return concat_path_s(cx_str(path1), cx_str(path2)).ptr; -} - -void pathbar_pathinput(Widget w, PathBar *p, XtPointer d) -{ - char *newpath = XNETextGetString(p->textfield); - if(newpath) { - if(newpath[0] == '~') { - char *p = newpath+1; - char *home = getenv("HOME"); - char *cp = ConcatPath(home, p); - XtFree(newpath); - newpath = cp; - } else if(newpath[0] != '/') { - char curdir[2048]; - curdir[0] = 0; - getcwd(curdir, 2048); - char *cp = ConcatPath(curdir, newpath); - XtFree(newpath); - newpath = cp; - } - - /* update path */ - PathBarSetPath(p, newpath); - if(p->updateDir) { - p->updateDir(p->updateDirData, newpath, -1); - } - XtFree(newpath); - - /* hide textfield and show path as buttons */ - XtUnmanageChild(p->textfield); - pathbar_resize(p->widget, p, NULL); - - if(p->focus_widget) { - XmProcessTraversal(p->focus_widget, XmTRAVERSE_CURRENT); - } - } -} - -void pathbar_shift_left(Widget w, PathBar *p, XtPointer d) -{ - p->shift--; - pathbar_resize(p->widget, p, NULL); -} - -void pathbar_shift_right(Widget w, PathBar *p, XtPointer d) -{ - if(p->shift < 0) { - p->shift++; - } - pathbar_resize(p->widget, p, NULL); -} - -static void pathTextEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) { - PathBar *pb = data; - if(event->type == KeyReleaseMask) { - if(event->xkey.keycode == 9) { - XtUnmanageChild(pb->textfield); - pathbar_resize(pb->widget, pb, NULL); - *dispatch = False; - } else if(event->xkey.keycode == 36) { - pathbar_pathinput(pb->textfield, pb, NULL); - *dispatch = False; - } - } -} - -PathBar* CreatePathBar(Widget parent, ArgList args, int n) -{ - PathBar *bar = (PathBar*)XtMalloc(sizeof(PathBar)); - bar->path = NULL; - bar->updateDir = NULL; - bar->updateDirData = NULL; - - bar->focus_widget = NULL; - - bar->getpathelm = NULL; - bar->getpathelmdata = NULL; - bar->current_pathelms = NULL; - - bar->shift = 0; - - XtSetArg(args[n], XmNmarginWidth, 0); n++; - XtSetArg(args[n], XmNmarginHeight, 0); n++; - bar->widget = XmCreateDrawingArea(parent, "pathbar", args, n); - XtAddCallback( - bar->widget, - XmNresizeCallback, - (XtCallbackProc)pathbar_resize, - bar); - XtAddCallback( - bar->widget, - XmNinputCallback, - (XtCallbackProc)pathbar_input, - bar); - - Arg a[4]; - XtSetArg(a[0], XmNshadowThickness, 0); - XtSetArg(a[1], XmNx, 0); - XtSetArg(a[2], XmNy, 0); - bar->textfield = XNECreateText(bar->widget, "pbtext", a, 3); - bar->input = 0; - XtAddCallback( - bar->textfield, - XmNlosingFocusCallback, - (XtCallbackProc)pathbar_losingfocus, - bar); - XtAddCallback(bar->textfield, XmNactivateCallback, - (XtCallbackProc)pathbar_pathinput, bar); - XtAddEventHandler(bar->textfield, KeyPressMask | KeyReleaseMask, FALSE, pathTextEH, bar); - - XtSetArg(a[0], XmNarrowDirection, XmARROW_LEFT); - bar->left = XmCreateArrowButton(bar->widget, "pbbutton", a, 1); - XtSetArg(a[0], XmNarrowDirection, XmARROW_RIGHT); - bar->right = XmCreateArrowButton(bar->widget, "pbbutton", a, 1); - XtAddCallback( - bar->left, - XmNactivateCallback, - (XtCallbackProc)pathbar_shift_left, - bar); - XtAddCallback( - bar->right, - XmNactivateCallback, - (XtCallbackProc)pathbar_shift_right, - bar); - - Pixel bg; - XtVaGetValues(bar->textfield, XmNbackground, &bg, NULL); - XtVaSetValues(bar->widget, XmNbackground, bg, NULL); - - XtManageChild(bar->left); - XtManageChild(bar->right); - - XtVaGetValues(bar->left, XmNwidth, &bar->lw, NULL); - XtVaGetValues(bar->right, XmNwidth, &bar->rw, NULL); - - bar->segmentAlloc = 16; - bar->numSegments = 0; - bar->pathSegments = (Widget*)XtCalloc(16, sizeof(Widget)); - - bar->selection = 0; - - return bar; -} - -void PathBarChangeDir(Widget w, PathBar *bar, XtPointer c) -{ - XmToggleButtonSetState(bar->pathSegments[bar->selection], False, False); - - int i; - for(i=0;i<bar->numSegments;i++) { - if(bar->pathSegments[i] == w) { - bar->selection = i; - XmToggleButtonSetState(w, True, False); - break; - } - } - - UiPathElm elm = bar->current_pathelms[i]; - cxmutstr path = cx_strdup(cx_strn(elm.path, elm.path_len)); - if(bar->updateDir) { - XNETextSetString(bar->textfield, path.ptr); - bar->updateDir(bar->updateDirData, path.ptr, i); - } - free(path.ptr); -} - -static void ui_pathelm_destroy(UiPathElm *elms, size_t nelm) { - for(int i=0;i<nelm;i++) { - free(elms[i].name); - free(elms[i].path); - } - free(elms); -} - -void PathBarSetPath(PathBar *bar, const char *path) -{ - if(bar->path) { - free(bar->path); - } - bar->path = strdup(path); - - for(int i=0;i<bar->numSegments;i++) { - XtDestroyWidget(bar->pathSegments[i]); - } - XtUnmanageChild(bar->textfield); - XtManageChild(bar->left); - XtManageChild(bar->right); - bar->input = False; - - Arg args[4]; - XmString str; - - bar->numSegments = 0; - - ui_pathelm_destroy(bar->current_pathelms, bar->numSegments); - size_t nelm = 0; - UiPathElm* path_elm = bar->getpathelm(bar->path, strlen(bar->path), &nelm, bar->getpathelmdata); - if (!path_elm) { - return; - } - bar->current_pathelms = path_elm; - bar->numSegments = nelm; - bar->pathSegments = realloc(bar->pathSegments, nelm * sizeof(Widget*)); - - for(int i=0;i<nelm;i++) { - UiPathElm elm = path_elm[i]; - - cxmutstr name = cx_strdup(cx_strn(elm.name, elm.name_len)); - str = XmStringCreateLocalized(elm.name); - free(name.ptr); - - XtSetArg(args[0], XmNlabelString, str); - XtSetArg(args[1], XmNfillOnSelect, True); - XtSetArg(args[2], XmNindicatorOn, False); - Widget button = XmCreateToggleButton(bar->widget, "pbbutton", args, 3); - XtAddCallback( - button, - XmNvalueChangedCallback, - (XtCallbackProc)PathBarChangeDir, - bar); - XmStringFree(str); - - bar->pathSegments[i] = button; - } - - bar->selection = bar->numSegments-1; - XmToggleButtonSetState(bar->pathSegments[bar->selection], True, False); - - XNETextSetString(bar->textfield, (char*)path); - XNETextSetInsertionPosition(bar->textfield, XNETextGetLastPosition(bar->textfield)); - - pathbar_resize(bar->widget, bar, NULL); -} - -void PathBarDestroy(PathBar *pathbar) { - if(pathbar->path) { - XtFree(pathbar->path); - } - XtFree((void*)pathbar->pathSegments); - XtFree((void*)pathbar); -} - - /* ---------------------------- Path Text Field ---------------------------- */ static void destroy_pathbar(Widget w, XtPointer *data, XtPointer d) { @@ -915,47 +520,6 @@ XtFree((void*)pathbar); } -// TODO: move to common -static UiPathElm* default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data) { - cxstring *pathelms; - size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), CX_STR("/"), 4096, &pathelms); - - if (nelm == 0) { - *ret_nelm = 0; - return NULL; - } - - UiPathElm* elms = (UiPathElm*)calloc(nelm, sizeof(UiPathElm)); - size_t n = nelm; - int j = 0; - for (int i = 0; i < nelm; i++) { - cxstring c = pathelms[i]; - if (c.length == 0) { - if (i == 0) { - c.length = 1; - } - else { - n--; - continue; - } - } - - cxmutstr m = cx_strdup(c); - elms[j].name = m.ptr; - elms[j].name_len = m.length; - - size_t elm_path_len = c.ptr + c.length - full_path; - cxmutstr elm_path = cx_strdup(cx_strn(full_path, elm_path_len)); - elms[j].path = elm_path.ptr; - elms[j].path_len = elm_path.length; - - j++; - } - *ret_nelm = n; - - return elms; -} - static void pathbar_activate(void *data, char *path, int index) { UiEventData *event = data; UiEvent evt; @@ -981,7 +545,7 @@ PathBar *pathbar = CreatePathBar(parent, xargs, n); if(!args->getpathelm) { - pathbar->getpathelm= default_pathelm_func; + pathbar->getpathelm= ui_default_pathelm_func; } else { pathbar->getpathelm = args->getpathelm; pathbar->getpathelmdata = args->getpathelmdata; @@ -1018,7 +582,7 @@ XtAddCallback( pathbar->widget, XmNdestroyCallback, - (XtCallbackProc)ui_destroy_eventdata, + (XtCallbackProc)ui_destroy_data, eventdata); }