add stylefunc for table/listview for row and cell styling options (GTK)

Sat, 30 Aug 2025 16:02:52 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sat, 30 Aug 2025 16:02:52 +0200
changeset 738
0dbf92544d4f
parent 737
ac7f0869e8ae
child 739
3236d5ed3a95

add stylefunc for table/listview for row and cell styling options (GTK)

application/main.c file | annotate | diff | comparison | revisions
ui/gtk/list.c file | annotate | diff | comparison | revisions
ui/gtk/list.h file | annotate | diff | comparison | revisions
ui/ui/toolkit.h file | annotate | diff | comparison | revisions
ui/ui/tree.h file | annotate | diff | comparison | revisions
--- a/application/main.c	Sat Aug 30 10:18:09 2025 +0200
+++ b/application/main.c	Sat Aug 30 16:02:52 2025 +0200
@@ -460,6 +460,17 @@
     return NULL;
 }
 
+UiBool table_getstyle(UiList *list, void *elm, int row, int col, void *userdata, UiTextStyle *style) {
+    if(row < 2 && col != -1) {
+        style->text_style = UI_TEXT_STYLE_BOLD;
+        style->fg.blue = col == 0 ? 255 : 0;
+        style->fg.green = col == 1 ? 255 : 0;
+        style->fg.red = col == 4 ? 255 : 0;
+        return TRUE;
+    }
+    return FALSE;
+}
+
 void sourcelist_getvalue(UiList *list, void *sublistdata, void *rowdata, int index, UiSubListItem *item, void *userdata) {
     item->label = strdup(rowdata);
     item->eventdata = sublistdata;
@@ -628,7 +639,7 @@
             UiModel *model = ui_model(obj->ctx, UI_STRING, "col1", UI_INTEGER, "col2", UI_ICON, "col3", UI_ICON_TEXT, "col4", UI_INTEGER, "col5", -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,
+                    .getvalue = table_getvalue, .getstyle = table_getstyle,
                     .onactivate = action_table_activate, .onactivatedata = "activate",
                     .onselection = action_table_activate, .onselectiondata = "selection");
             ui_hbox(obj, .fill = FALSE) {
--- a/ui/gtk/list.c	Sat Aug 30 10:18:09 2025 +0200
+++ b/ui/gtk/list.c	Sat Aug 30 16:02:52 2025 +0200
@@ -92,6 +92,12 @@
     tableview->ondropdata = args->ondropdata;
     tableview->selection.count = 0;
     tableview->selection.rows = NULL;
+    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
     
     if(args->getvalue2) {
         tableview->getvalue = args->getvalue2;
@@ -102,7 +108,7 @@
     } else {
         tableview->getvalue = null_getvalue;
     }
-    
+      
     return tableview;
 }
 
@@ -163,6 +169,28 @@
     }
 }
 
+PangoAttrList* textstyle2pangoattributes(UiTextStyle style) {
+    PangoAttrList *attr = pango_attr_list_new();
+    
+    if(style.text_style & UI_TEXT_STYLE_BOLD) {
+        pango_attr_list_insert(attr, pango_attr_weight_new(PANGO_WEIGHT_BOLD));
+    }
+    if(style.text_style & UI_TEXT_STYLE_ITALIC) {
+        pango_attr_list_insert(attr, pango_attr_style_new(PANGO_STYLE_ITALIC));
+    }
+    if(style.text_style & UI_TEXT_STYLE_UNDERLINE) {
+        pango_attr_list_insert(attr, pango_attr_underline_new(PANGO_UNDERLINE_SINGLE));
+    }
+    
+    // foreground color, convert from 8bit to 16bit
+    guint16 r = (guint16)style.fg.red   * 257;
+    guint16 g = (guint16)style.fg.green * 257;
+    guint16 b = (guint16)style.fg.blue  * 257;
+    pango_attr_list_insert(attr, pango_attr_foreground_new(r, g, b));
+    
+    return attr;
+}
+
 static void column_factory_bind(GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) {
     UiColData *col = userdata;
     UiList *list = col->listview->var ? col->listview->var->value : NULL;
@@ -176,6 +204,34 @@
     void *data = listview->getvalue(list, obj->data, obj->i, col->data_column, listview->getvaluedata, &freevalue);
     GtkWidget *child = gtk_list_item_get_child(item);
     
+    PangoAttrList *attributes = NULL;
+    UiTextStyle style = { 0, 0 };
+    if(listview->getstyle) { 
+        // query current row style, if it wasn't already queried
+        if(obj->i != listview->current_row) {
+            listview->current_row = obj->i;
+            listview->row_style = (UiTextStyle){ 0, 0 };
+            listview->apply_row_style = listview->getstyle(list, obj->data, obj->i, -1, listview->getstyledata, &listview->row_style);
+            style = listview->row_style;
+            if(listview->apply_row_style) {
+                pango_attr_list_unref(listview->current_row_attributes);
+                listview->current_row_attributes = textstyle2pangoattributes(style);
+            }
+        }
+        
+        int style_col = col->data_column;
+        if(type == UI_ICON_TEXT || type == UI_ICON_TEXT_FREE) {
+            style_col++; // col->data_column is the icon, we need the next col for the label
+        }
+        
+        // get the column style
+        if(listview->getstyle(list, obj->data, obj->i, style_col, listview->getstyledata, &style)) {
+            attributes = textstyle2pangoattributes(style);
+        } else if(listview->apply_row_style) {
+            attributes = listview->current_row_attributes;
+        }
+    }    
+    
     switch(type) {
         case UI_STRING_FREE: {
             freevalue = TRUE;
@@ -185,6 +241,7 @@
             if(freevalue) {
                 free(data);
             }
+            gtk_label_set_attributes(GTK_LABEL(child), attributes);
             break;
         }
         case UI_INTEGER: {
@@ -192,6 +249,7 @@
             char buf[32];
             snprintf(buf, 32, "%d", (int)intvalue);
             gtk_label_set_label(GTK_LABEL(child), buf);
+            gtk_label_set_attributes(GTK_LABEL(child), attributes);
             break;
         }
         case UI_ICON: {
@@ -217,6 +275,7 @@
             }
             if(data2 && label) {
                 gtk_label_set_label(GTK_LABEL(label), data2);
+                gtk_label_set_attributes(GTK_LABEL(label), attributes);
             }
             if(freevalue) {
                 free(data2);
@@ -224,6 +283,10 @@
             break;
         }
     }
+    
+    if(attributes != listview->current_row_attributes) {
+        pango_attr_list_unref(attributes);
+    }
 }
 
 static GtkSelectionModel* create_selection_model(UiListView *listview, GListStore *liststore, bool multiselection) {
@@ -1676,6 +1739,8 @@
     }
 #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);
 #endif
     free(v->selection.rows);
     free(v);
--- a/ui/gtk/list.h	Sat Aug 30 10:18:09 2025 +0200
+++ b/ui/gtk/list.h	Sat Aug 30 16:02:52 2025 +0200
@@ -47,12 +47,19 @@
     UiModel           *model;
     ui_getvaluefunc2  getvalue;
     void              *getvaluedata;
+    ui_stylefunc      getstyle;
+    void              *getstyledata;
     char              **elements;
     size_t            nelm;
+    int               current_row;
+    UiTextStyle       row_style;
+    UiBool            apply_row_style;
 #if GTK_CHECK_VERSION(4, 10, 0)
     GListStore        *liststore;
     GtkSelectionModel *selectionmodel;
     UiColData         *columns;
+    PangoAttrList     *default_attributes; // TODO: remove
+    PangoAttrList     *current_row_attributes;
 #endif
     ui_callback       onactivate;
     void              *onactivatedata;
--- a/ui/ui/toolkit.h	Sat Aug 30 10:18:09 2025 +0200
+++ b/ui/ui/toolkit.h	Sat Aug 30 16:02:52 2025 +0200
@@ -200,6 +200,9 @@
 
 typedef struct UiListSelection UiListSelection;
 
+typedef struct UiTextStyle  UiTextStyle;
+typedef struct UiColor      UiColor;
+
 /* begin opaque types */
 typedef struct UiContext     UiContext;
 typedef struct UiContainer   UiContainer;
@@ -251,6 +254,7 @@
 
 typedef void*(*ui_getvaluefunc)(void *elm, int col);
 typedef void*(*ui_getvaluefunc2)(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult);
+typedef UiBool(*ui_stylefunc)(UiList *list, void *elm, int row, int col, void *userdata, UiTextStyle *style);
 
 typedef int(*ui_threadfunc)(void*);
 
@@ -502,6 +506,22 @@
     UI_EVENT_DATA_FILE_LIST
 };
 
+#define UI_COLOR(r, g, b) (UiColor){r, g, b}
+struct UiColor {
+    uint8_t red;
+    uint8_t green;
+    uint8_t blue;
+};
+
+#define UI_TEXT_STYLE_BOLD      1
+#define UI_TEXT_STYLE_ITALIC    2
+#define UI_TEXT_STYLE_UNDERLINE 4
+
+struct UiTextStyle {
+    uint32_t text_style;
+    UiColor  fg;
+};
+
 
 UIEXPORT void ui_init(const char *appname, int argc, char **argv);
 UIEXPORT const char* ui_appname();
--- a/ui/ui/tree.h	Sat Aug 30 10:18:09 2025 +0200
+++ b/ui/ui/tree.h	Sat Aug 30 16:02:52 2025 +0200
@@ -120,6 +120,8 @@
     ui_getvaluefunc getvalue;
     ui_getvaluefunc2 getvalue2;
     void *getvalue2data;
+    ui_stylefunc getstyle;
+    void *getstyledata;
     ui_callback onactivate;
     void* onactivatedata;
     ui_callback onselection;

mercurial