ui/gtk/list.c

changeset 786
150a1180f7ec
parent 781
b15ada8bdd8f
child 801
e096c441e874
--- a/ui/gtk/list.c	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/gtk/list.c	Thu Oct 02 14:49:27 2025 +0200
@@ -95,9 +95,8 @@
     tableview->current_row = -1;
     tableview->getstyle = args->getstyle;
     tableview->getstyledata = args->getstyledata;
-#if GTK_CHECK_VERSION(4, 10, 0)
-    tableview->default_attributes = pango_attr_list_new();
-#endif
+    tableview->onsave = args->onsave;
+    tableview->onsavedata = args->onsavedata;
     
     if(args->getvalue2) {
         tableview->getvalue = args->getvalue2;
@@ -146,6 +145,58 @@
 
 /* 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;
+        value.type = UI_STRING_EDITABLE;
+        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;
@@ -162,6 +213,38 @@
     } else if(type == UI_ICON) {
         GtkWidget *image = gtk_image_new();
         gtk_list_item_set_child(item, image);
+    } else if(type == UI_STRING_EDITABLE) {
+        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);
@@ -296,6 +379,17 @@
             }
             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;
+        }
     }
     
     if(attributes != listview->current_row_attributes) {
@@ -315,6 +409,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;
+    }
 }
     
 
@@ -1908,7 +2011,6 @@
     }
 #if GTK_CHECK_VERSION(4, 10, 0)
     free(v->columns);
-    pango_attr_list_unref(v->default_attributes);
     pango_attr_list_unref(v->current_row_attributes);
     cxMapFree(v->bound_rows);
 #endif
@@ -2182,7 +2284,7 @@
     }
 }
 
-static GtkWidget* create_listbox_row(UiListBox *listbox, UiListBoxSubList *sublist, UiSubListItem *item, int index) {
+static void listbox_fill_row(UiListBox *listbox, GtkWidget *row, UiListBoxSubList *sublist, UiSubListItem *item, int index) {
     GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
     if(item->icon) {
         GtkWidget *icon = ICON_IMAGE(item->icon);
@@ -2194,7 +2296,6 @@
     if(item->badge) {
         
     }
-    GtkWidget *row = gtk_list_box_row_new();
     LISTBOX_ROW_SET_CHILD(row, hbox);
     
     // signals 
@@ -2245,8 +2346,36 @@
                 event
                 );
     }
+}
+
+static void update_sublist_item(UiListBox *listbox, UiListBoxSubList *sublist, int index) {
+    GtkListBoxRow *row = gtk_list_box_get_row_at_index(listbox->listbox, sublist->startpos + index);
+    if(!row) {
+        return;
+    }
+    UiList *list = sublist->var->value;
+    if(!list) {
+        return;
+    }
     
-    return row;
+    void *elm = list->get(list, index);
+    UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL };
+    if(listbox->getvalue) {
+        listbox->getvalue(list, sublist->userdata, elm, index, &item, listbox->getvaluedata);
+    } else {
+        item.label = strdup(elm);
+    }
+    
+    LISTBOX_ROW_REMOVE_CHILD(row);
+    
+    listbox_fill_row(listbox, GTK_WIDGET(row), sublist, &item, index);
+    
+     // cleanup
+    free(item.label);
+    free(item.icon);
+    free(item.button_label);
+    free(item.button_icon);
+    free(item.badge);
 }
 
 void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, size_t listbox_insert_index) {
@@ -2293,7 +2422,8 @@
         }
         
         // create listbox item
-        GtkWidget *row = create_listbox_row(listbox, sublist, &item, (int)index);
+        GtkWidget *row = gtk_list_box_row_new();
+        listbox_fill_row(listbox, row, sublist, &item, (int)index);
         if(index == 0) {
             // first row in the sublist, set ui_listbox data to the row
             // which is then used by the headerfunc
@@ -2327,14 +2457,17 @@
 
 void ui_listbox_list_update(UiList *list, int i) {
     UiListBoxSubList *sublist = list->obj;
-    ui_listbox_update_sublist(sublist->listbox, sublist, sublist->startpos);
-    size_t pos = 0;
-    CxIterator it = cxListIterator(sublist->listbox->sublists);
-    cx_foreach(UiListBoxSubList *, ls, it) {
-        ls->startpos = pos;
-        pos += sublist->numitems;
+    if(i < 0) {
+        ui_listbox_update_sublist(sublist->listbox, sublist, sublist->startpos);
+        size_t pos = 0;
+        CxIterator it = cxListIterator(sublist->listbox->sublists);
+        cx_foreach(UiListBoxSubList *, ls, it) {
+            ls->startpos = pos;
+            pos += ls->numitems;
+        }
+    } else {
+        update_sublist_item(sublist->listbox, sublist, i);
     }
-    
 }
 
 void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) {

mercurial