# HG changeset patch # User Olaf Wintermann # Date 1759169767 -7200 # Node ID 85b6cef7fcba62b2802b1610d5662f66c1fece26 # Parent 622efebfab37eaf1de81c0694e4eacc0ea9e26ad implement listview onsave handler (GTK) diff -r 622efebfab37 -r 85b6cef7fcba application/main.c --- a/application/main.c Sun Sep 28 20:09:08 2025 +0200 +++ b/application/main.c Mon Sep 29 20:16:07 2025 +0200 @@ -570,6 +570,11 @@ ui_linkbutton_value_set(doc->link, label, uri); } +static UiBool list_save(UiList *list, int row, int col, UiCellValue value, void *userdata) { + printf("list new value at [%d, %d]: %s\n", row, col, value.string); + return FALSE; +} + void application_startup(UiEvent *event, void *data) { // global list UiContext *global = ui_global_context(); @@ -658,7 +663,7 @@ UiModel *model = ui_model(obj->ctx, UI_ICON_TEXT, "col1", UI_INTEGER, "col2", UI_ICON, "col3", UI_ICON_TEXT, "col4", UI_INTEGER, "col5", UI_STRING_EDITABLE, "edit6", UI_STRING_EDITABLE, "edit7", -1); model->columnsize[0] = -1; ui_table(obj, .model = model, .list = doc->list2, .colspan = 2, .fill = TRUE, .contextmenu = menubuilder, .multiselection = TRUE, .fill = TRUE, - .getvalue = table_getvalue, .getstyle = table_getstyle, + .getvalue = table_getvalue, .getstyle = table_getstyle, .onsave = list_save, .onactivate = action_table_activate, .onactivatedata = "activate", .onselection = action_table_activate, .onselectiondata = "selection"); ui_hbox(obj, .fill = FALSE) { diff -r 622efebfab37 -r 85b6cef7fcba ui/gtk/list.c --- a/ui/gtk/list.c Sun Sep 28 20:09:08 2025 +0200 +++ b/ui/gtk/list.c Mon Sep 29 20:16:07 2025 +0200 @@ -95,6 +95,8 @@ tableview->current_row = -1; tableview->getstyle = args->getstyle; tableview->getstyledata = args->getstyledata; + tableview->onsave = args->onsave; + tableview->onsavedata = args->onsavedata; if(args->getvalue2) { tableview->getvalue = args->getvalue2; @@ -143,6 +145,57 @@ /* END GObject wrapper for generic pointers */ +typedef struct UiCellEntry { + GtkEntry *entry; + UiListView *listview; + char *previous_value; + int row; + int col; +} UiCellEntry; + +static void cell_save_value(UiCellEntry *data, int restore) { + if(data->listview && data->listview->onsave) { + UiVar *var = data->listview->var; + UiList *list = var ? var->value : NULL; + const char *str = ENTRY_GET_TEXT(data->entry); + UiCellValue value; + value.string = str; + if(data->listview->onsave(list, data->row, data->col, value, data->listview->onsavedata)) { + free(data->previous_value); + data->previous_value = strdup(str); + } else if(restore) { + ENTRY_SET_TEXT(data->entry, data->previous_value); + } + } +} + +static void cell_entry_leave_focus( + GtkEventControllerFocus *self, + UiCellEntry *data) +{ + // TODO: use a different singal to track focus + // we only want to call cell_save_value, when another entry is selected, + // not when the window loses focus or something like that + cell_save_value(data, TRUE); +} + +static void cell_entry_destroy(GtkWidget *object, UiCellEntry *data) { + free(data->previous_value); + free(data); +} + +static void cell_entry_unmap(GtkWidget *w, UiCellEntry *data) { + const char *text = ENTRY_GET_TEXT(w); + cell_save_value(data, FALSE); +} + +static void cell_entry_activate( + GtkEntry *self, + UiCellEntry *data) +{ + cell_save_value(data, TRUE); +} + static void column_factory_setup(GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) { UiColData *col = userdata; UiModel *model = col->listview->model; @@ -163,6 +216,34 @@ GtkWidget *textfield = gtk_entry_new(); gtk_widget_add_css_class(textfield, "ui-table-entry"); gtk_list_item_set_child(item, textfield); + + UiCellEntry *entry_data = malloc(sizeof(UiCellEntry)); + entry_data->entry = GTK_ENTRY(textfield); + entry_data->listview = NULL; + entry_data->previous_value = NULL; + entry_data->col = 0; + entry_data->row = 0; + g_object_set_data(G_OBJECT(textfield), "ui_entry_data", entry_data); + + g_signal_connect( + textfield, + "destroy", + G_CALLBACK(cell_entry_destroy), + entry_data); + g_signal_connect( + textfield, + "activate", + G_CALLBACK(cell_entry_activate), + entry_data); + g_signal_connect( + textfield, + "unmap", + G_CALLBACK(cell_entry_unmap), + entry_data); + + GtkEventController *focus_controller = gtk_event_controller_focus_new(); + g_signal_connect(focus_controller, "leave", G_CALLBACK(cell_entry_leave_focus), entry_data); + gtk_widget_add_controller(textfield, focus_controller); } else { GtkWidget *label = gtk_label_new(NULL); gtk_label_set_xalign(GTK_LABEL(label), 0); @@ -298,6 +379,13 @@ break; } case UI_STRING_EDITABLE: { + UiCellEntry *entry = g_object_get_data(G_OBJECT(child), "ui_entry_data"); + if(entry) { + entry->listview = col->listview; + entry->row = obj->i; + entry->col = col->data_column; + entry->previous_value = strdup(data); + } ENTRY_SET_TEXT(child, data); break; } @@ -320,6 +408,15 @@ cxMapRemove(listview->bound_rows, row_key); } } // else: should not happen + + GtkWidget *child = gtk_list_item_get_child(item); + UiCellEntry *entry = g_object_get_data(G_OBJECT(child), "ui_entry_data"); + if(entry) { + cell_save_value(entry, FALSE); + entry->listview = NULL; + free(entry->previous_value); + entry->previous_value = NULL; + } } diff -r 622efebfab37 -r 85b6cef7fcba ui/gtk/list.h --- a/ui/gtk/list.h Sun Sep 28 20:09:08 2025 +0200 +++ b/ui/gtk/list.h Mon Sep 29 20:16:07 2025 +0200 @@ -81,6 +81,8 @@ void *ondragcompletedata; ui_callback ondrop; void *ondropdata; + ui_list_savefunc onsave; + void *onsavedata; UiListSelection selection; } UiListView; diff -r 622efebfab37 -r 85b6cef7fcba ui/ui/tree.h --- a/ui/ui/tree.h Sun Sep 28 20:09:08 2025 +0200 +++ b/ui/ui/tree.h Mon Sep 29 20:16:07 2025 +0200 @@ -55,6 +55,13 @@ UI_STRING_EDITABLE } UiModelType; +typedef union UiCellValue { + const char *string; + int64_t i; +} UiCellValue; + +typedef UiBool (*ui_list_savefunc)(UiList *list, int row, int col, UiCellValue value, void *userdata); + struct UiModel { /* * number of columns @@ -135,6 +142,8 @@ void* ondropdata; UiBool multiselection; UiMenuBuilder *contextmenu; + ui_list_savefunc onsave; + void *onsavedata; const int *groups; };