ui/gtk/list.c

changeset 1246
43a7874a9d0e
parent 1245
4163776eef49
child 1247
070d0472e6af
equal deleted inserted replaced
1245:4163776eef49 1246:43a7874a9d0e
3044 free(v->onactivate_action); 3044 free(v->onactivate_action);
3045 free(v->onbuttonclick_action); 3045 free(v->onbuttonclick_action);
3046 free(v); 3046 free(v);
3047 } 3047 }
3048 3048
3049 static void ui_destroy_listbox2_row(UiListBox2Row *row) {
3050 cxListFree(row->children);
3051 }
3052
3049 UIWIDGET ui_sourcelist2_create(UiObject *obj, UiSourceList2Args *args) { 3053 UIWIDGET ui_sourcelist2_create(UiObject *obj, UiSourceList2Args *args) {
3050 #ifdef UI_GTK3 3054 #ifdef UI_GTK3
3051 GtkWidget *listbox = g_object_new(ui_sidebar_list_box_get_type(), NULL); 3055 GtkWidget *listbox = g_object_new(ui_sidebar_list_box_get_type(), NULL);
3052 #else 3056 #else
3053 GtkWidget *listbox = gtk_list_box_new(); 3057 GtkWidget *listbox = gtk_list_box_new();
3069 ct->add(ct, scroll_area, &layout); 3073 ct->add(ct, scroll_area, &layout);
3070 3074
3071 UiListBox2 *uilistbox = malloc(sizeof(UiListBox2)); 3075 UiListBox2 *uilistbox = malloc(sizeof(UiListBox2));
3072 uilistbox->obj = obj; 3076 uilistbox->obj = obj;
3073 uilistbox->listbox = GTK_LIST_BOX(listbox); 3077 uilistbox->listbox = GTK_LIST_BOX(listbox);
3074 uilistbox->rows = cxArrayListCreate(NULL, CX_STORE_POINTERS, 64); 3078 uilistbox->rows = cxArrayListCreate(NULL, sizeof(UiListBox2Row), 32);
3079 cxSetDestructor(uilistbox->rows, ui_destroy_listbox2_row);
3075 uilistbox->getvalue = args->getvalue; 3080 uilistbox->getvalue = args->getvalue;
3076 uilistbox->getvaluedata = args->getvaluedata; 3081 uilistbox->getvaluedata = args->getvaluedata;
3077 uilistbox->getdepth = args->getdepth;
3078 uilistbox->getdepthdata = args->getdepthdata;
3079 uilistbox->onactivate = args->onactivate; 3082 uilistbox->onactivate = args->onactivate;
3080 uilistbox->onactivatedata = args->onactivatedata; 3083 uilistbox->onactivatedata = args->onactivatedata;
3081 uilistbox->onactivate_action = args->onactivate_action ? strdup(args->onactivate_action) : NULL; 3084 uilistbox->onactivate_action = args->onactivate_action ? strdup(args->onactivate_action) : NULL;
3082 uilistbox->onbuttonclick = args->onbuttonclick; 3085 uilistbox->onbuttonclick = args->onbuttonclick;
3083 uilistbox->onbuttonclickdata = args->onbuttonclickdata; 3086 uilistbox->onbuttonclickdata = args->onbuttonclickdata;
3109 listbox, 3112 listbox,
3110 "destroy", 3113 "destroy",
3111 G_CALLBACK(ui_destroy_sourcelist2), 3114 G_CALLBACK(ui_destroy_sourcelist2),
3112 uilistbox); 3115 uilistbox);
3113 3116
3114 if(args->onactivate) { 3117 if(args->onactivate || args->onactivate_action) {
3115 g_signal_connect( 3118 g_signal_connect(
3116 listbox, 3119 listbox,
3117 "row-activated", 3120 "row-activated",
3118 G_CALLBACK(ui_listbox2_row_activate), 3121 G_CALLBACK(ui_listbox2_row_activate),
3119 NULL); 3122 NULL);
3120 } 3123 }
3121 3124
3122 return scroll_area; 3125 return scroll_area;
3123 } 3126 }
3124 3127
3125 3128 static void listbox2_button_clicked(GtkWidget *button, UiListBox2Row *data) {
3126 static int listbox2_update_item(UiListBox2 *listbox, GtkWidget *row, UiList *list, void *elm, int index) { 3129 UiEvent event;
3127 UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL }; 3130 event.obj = data->listbox->obj;
3128 if(listbox->getvalue) { 3131 event.window = event.obj->window;
3129 listbox->getvalue(list,elm, index, &item, listbox->getvaluedata); 3132 event.document = event.obj->ctx->document;
3130 } 3133 event.eventdata = NULL;
3131 3134 event.eventdatatype = UI_EVENT_DATA_NULL;
3132 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10); 3135 event.intval = data->index;
3133 3136 event.set = ui_get_setop();
3134 return 0; 3137
3138 if(data->listbox->onbuttonclick) {
3139 data->listbox->onbuttonclick(&event, data->listbox->onbuttonclickdata);
3140 }
3141
3142 if(data->listbox->onbuttonclick_action) {
3143 uic_action_callback(&event, data->listbox->onbuttonclick_action);
3144 }
3145 }
3146
3147
3148 static void listbox2_node_children_update_visibility(UiListBox2Row *rowdata, int visible) {
3149 CxIterator i = cxListIterator(rowdata->children);
3150 int child_visible = visible ? rowdata->expanded : 0;
3151 cx_foreach(UiListBox2Row *, child, i) {
3152 gtk_widget_set_visible(child->row, child_visible);
3153 listbox2_node_children_update_visibility(child, child_visible);
3154 }
3155 }
3156
3157 #if GTK_CHECK_VERSION(4, 0, 0)
3158
3159 void listbox2_node_expand(
3160 GtkGestureClick *self,
3161 gint n_press,
3162 gdouble x,
3163 gdouble y,
3164 UiListBox2Row *rowdata)
3165 {
3166 rowdata->expanded = !rowdata->expanded;
3167 if(rowdata->expanded) {
3168 gtk_image_set_from_icon_name(GTK_IMAGE(rowdata->expander), "pan-down-symbolic");
3169 } else {
3170 gtk_image_set_from_icon_name(GTK_IMAGE(rowdata->expander), "pan-end-symbolic");
3171 }
3172 listbox2_node_children_update_visibility(rowdata, rowdata->expanded);
3173 }
3174
3175 #endif
3176 #if GTK_MAJOR_VERSION == 3
3177
3178 #endif
3179
3180 #define LISTBOX2_ITEM_HBOX_SPACING 10
3181 static void listbox2_update_item(UiListBox2Row *rowdata, GtkWidget *row, UiSourceListItem *item, int identation) {
3182 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, LISTBOX2_ITEM_HBOX_SPACING);
3183
3184 rowdata->expanded = 1;
3185 GtkWidget *expander = gtk_image_new_from_icon_name("pan-down-symbolic");
3186 int exp_width = 0;
3187 #if GTK_CHECK_VERSION(4, 0, 0)
3188 gtk_widget_measure(expander,
3189 GTK_ORIENTATION_HORIZONTAL,
3190 -1,
3191 &exp_width,
3192 NULL,
3193 NULL,
3194 NULL);
3195
3196 GtkGesture *click = gtk_gesture_click_new();
3197 gtk_widget_add_controller(expander, GTK_EVENT_CONTROLLER(click));
3198 g_signal_connect(click, "released", G_CALLBACK(listbox2_node_expand), rowdata);
3199 #else
3200 int exp_height;
3201 gtk_widget_get_preferred_width(expander, &exp_width, &exp_height);
3202 #endif
3203 gtk_widget_set_margin_start(hbox, identation * (exp_width+LISTBOX2_ITEM_HBOX_SPACING));
3204
3205 WIDGET_NO_SHOW_ALL(expander);
3206 gtk_widget_set_visible(expander, FALSE);
3207 // normal items have the expander icon in the front
3208 if(!item->is_header) {
3209 BOX_ADD(hbox, expander);
3210 }
3211 rowdata->expander = expander;
3212
3213 if(item->icon) {
3214 GtkWidget *icon = ICON_IMAGE(item->icon);
3215 BOX_ADD(hbox, icon);
3216 }
3217 GtkWidget *label = gtk_label_new(item->label);
3218 gtk_widget_set_halign(label, GTK_ALIGN_START);
3219
3220 BOX_ADD_EXPAND(hbox, label);
3221
3222 // button
3223 if(item->button_label || item->button_icon) {
3224 GtkWidget *button = gtk_button_new();
3225 gtk_button_set_label(GTK_BUTTON(button), item->button_label);
3226 ui_button_set_icon_name(button, item->button_icon);
3227 WIDGET_ADD_CSS_CLASS(button, "flat");
3228 BOX_ADD(hbox, button);
3229 g_signal_connect(
3230 button,
3231 "clicked",
3232 G_CALLBACK(listbox2_button_clicked),
3233 rowdata);
3234 gtk_widget_set_visible(button, FALSE);
3235 WIDGET_NO_SHOW_ALL(button);
3236 }
3237
3238 // headings have the expander at the end
3239 if(item->is_header) {
3240 BOX_ADD(hbox, expander);
3241 WIDGET_ADD_CSS_CLASS(label, "ui-listbox-header-row");
3242 rowdata->heading = TRUE;
3243 }
3244
3245 LISTBOX_ROW_SET_CHILD(row, hbox);
3246 g_object_set_data(G_OBJECT(row), "ui-listbox-row-data", rowdata);
3135 } 3247 }
3136 3248
3137 static void listbox2_update_all(UiListBox2 *listbox) { 3249 static void listbox2_update_all(UiListBox2 *listbox) {
3138 gtk_list_box_remove_all(listbox->listbox); 3250 gtk_list_box_remove_all(listbox->listbox);
3139 cxListClear(listbox->rows); 3251 cxListClear(listbox->rows);
3143 UiList *list = listbox->var->value; 3255 UiList *list = listbox->var->value;
3144 if(!list) { 3256 if(!list) {
3145 return; 3257 return;
3146 } 3258 }
3147 3259
3260 UiListBox2Row **stack;
3261 size_t stack_size = 16;
3262 stack = calloc(stack_size, sizeof(UiListBox2Row*));
3148 3263
3149 int index = 0; 3264 int index = 0;
3265 UiBool heading = FALSE;
3150 void *elm = list->first(list); 3266 void *elm = list->first(list);
3151 while(elm) { 3267 while(elm) {
3268 UiSourceListItem item = { 0 };
3269 if(listbox->getvalue) {
3270 listbox->getvalue(list, elm, index, &item, listbox->getvaluedata);
3271 }
3272
3152 GtkWidget *row = gtk_list_box_row_new(); 3273 GtkWidget *row = gtk_list_box_row_new();
3153 cxListAdd(listbox->rows, row);
3154 gtk_list_box_append(listbox->listbox, row); 3274 gtk_list_box_append(listbox->listbox, row);
3155 int node_depth = listbox2_update_item(listbox, row, list, elm, index); 3275 UiListBox2Row row_data = {0};
3276 row_data.listbox = listbox;
3277 row_data.row = row;
3278 row_data.index = index;
3279 row_data.depth = item.depth;
3280 cxListAdd(listbox->rows, &row_data);
3281 UiListBox2Row *row_data_ptr = cxListLast(listbox->rows);
3282 listbox2_update_item(row_data_ptr, row, &item, item.depth - (item.depth > 0 ? heading : 0));
3283
3284 if(item.depth >= stack_size) {
3285 stack_size += 8;
3286 stack = realloc(stack, stack_size * sizeof(UiListBox2Row*));
3287 }
3288 stack[item.depth] = row_data_ptr;
3289
3290 if(item.depth > 0) {
3291 // add row to parent
3292 UiListBox2Row *parent = item.depth-1 < stack_size ? stack[item.depth-1] : NULL;
3293 if(parent) {
3294 if(!parent->children) {
3295 parent->children = cxArrayListCreate(NULL, CX_STORE_POINTERS, 8);
3296 gtk_widget_set_visible(parent->expander, TRUE);
3297 WIDGET_ENABLE_SHOW_ALL(parent->expander);
3298 }
3299 cxListAdd(parent->children, row_data_ptr);
3300 } else {
3301 fprintf(stderr, "Error: sourcelist2: illegal depth: no parent found\n");
3302 }
3303 } else {
3304 heading = item.is_header != 0;
3305 }
3306
3307 free(item.label);
3308 free(item.icon);
3309 free(item.badge);
3310 free(item.button_label);
3311 free(item.button_icon);
3156 3312
3157 elm = list->next(list); 3313 elm = list->next(list);
3158 index++; 3314 index++;
3159 } 3315 }
3316
3317 cxListShrink(listbox->rows);
3318 free(stack);
3160 } 3319 }
3161 3320
3162 void ui_listbox2_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) { 3321 void ui_listbox2_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) {
3163 3322 UiListBox2Row *rowdata = g_object_get_data(G_OBJECT(row), "ui-listbox-row-data");
3323 if(!rowdata) {
3324 return;
3325 }
3326 UiListBox2 *listbox = rowdata->listbox;
3327 UiVar *var = listbox->var;
3328 if(!var) {
3329 return; // shouldn't happen
3330 }
3331
3332 UiList *list = var->value;
3333 void *elm = list->get(list, rowdata->index);
3334
3335 UiEvent event;
3336 event.obj = listbox->obj;
3337 event.window = event.obj->window;
3338 event.document = event.obj->ctx->document;
3339 event.eventdatatype = UI_EVENT_DATA_LIST_ELM;
3340 event.eventdata = elm;
3341 event.intval = rowdata->index;
3342 event.set = ui_get_setop();
3343
3344 if(listbox->onactivate) {
3345 listbox->onactivate(&event, listbox->onactivatedata);
3346 }
3347 if(listbox->onactivate_action) {
3348 uic_action_callback(&event, listbox->onactivate_action);
3349 }
3164 } 3350 }
3165 3351
3166 3352
3167 void ui_listbox2_update(UiList *list, int i) { 3353 void ui_listbox2_update(UiList *list, int i) {
3168 UiListBox2 *listbox = list->obj; 3354 UiListBox2 *listbox = list->obj;
3169 if(i >= 0) { 3355 if(i >= 0) {
3170 // TODO 3356 // TODO: update single row
3357 listbox2_update_all(listbox);
3171 } else { 3358 } else {
3172 listbox2_update_all(listbox); 3359 listbox2_update_all(listbox);
3173 } 3360 }
3174 } 3361 }
3175 3362

mercurial