ui/gtk/list.c

changeset 112
c3f2f16fa4b8
parent 110
c00e968d018b
child 113
dde28a806552
equal deleted inserted replaced
111:81c4f73236a4 112:c3f2f16fa4b8
243 entry_data); 243 entry_data);
244 244
245 GtkEventController *focus_controller = gtk_event_controller_focus_new(); 245 GtkEventController *focus_controller = gtk_event_controller_focus_new();
246 g_signal_connect(focus_controller, "leave", G_CALLBACK(cell_entry_leave_focus), entry_data); 246 g_signal_connect(focus_controller, "leave", G_CALLBACK(cell_entry_leave_focus), entry_data);
247 gtk_widget_add_controller(textfield, focus_controller); 247 gtk_widget_add_controller(textfield, focus_controller);
248 } else { 248 } else if(type == UI_BOOL_EDITABLE) {
249 GtkWidget *checkbox = gtk_check_button_new();
250 gtk_list_item_set_child(item, checkbox);
251 }else {
249 GtkWidget *label = gtk_label_new(NULL); 252 GtkWidget *label = gtk_label_new(NULL);
250 gtk_label_set_xalign(GTK_LABEL(label), 0); 253 gtk_label_set_xalign(GTK_LABEL(label), 0);
251 gtk_list_item_set_child(item, label); 254 gtk_list_item_set_child(item, label);
252 } 255 }
253 } 256 }
388 entry->previous_value = strdup(data); 391 entry->previous_value = strdup(data);
389 } 392 }
390 ENTRY_SET_TEXT(child, data); 393 ENTRY_SET_TEXT(child, data);
391 break; 394 break;
392 } 395 }
396 case UI_BOOL_EDITABLE: {
397 intptr_t i = (intptr_t)data;
398 gtk_check_button_set_active(GTK_CHECK_BUTTON(child), (gboolean)i);
399 break;
400 }
393 } 401 }
394 402
395 if(attributes != listview->current_row_attributes) { 403 if(attributes != listview->current_row_attributes) {
396 pango_attr_list_unref(attributes); 404 pango_attr_list_unref(attributes);
397 } 405 }
415 if(entry) { 423 if(entry) {
416 cell_save_value(entry, FALSE); 424 cell_save_value(entry, FALSE);
417 entry->listview = NULL; 425 entry->listview = NULL;
418 free(entry->previous_value); 426 free(entry->previous_value);
419 entry->previous_value = NULL; 427 entry->previous_value = NULL;
428 } else if(GTK_IS_CHECK_BUTTON(child)) {
429
420 } 430 }
421 } 431 }
422 432
423 433
424 static GtkSelectionModel* create_selection_model(UiListView *listview, GListStore *liststore, bool multiselection) { 434 static GtkSelectionModel* create_selection_model(UiListView *listview, GListStore *liststore, bool multiselection) {
433 g_signal_connect(selection_model, "selection-changed", G_CALLBACK(ui_listview_selection_changed), listview); 443 g_signal_connect(selection_model, "selection-changed", G_CALLBACK(ui_listview_selection_changed), listview);
434 return selection_model; 444 return selection_model;
435 } 445 }
436 446
437 UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) { 447 UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) {
438 UiObject* current = uic_current_obj(obj);
439
440 // to simplify things and share code with ui_table_create, we also 448 // to simplify things and share code with ui_table_create, we also
441 // use a UiModel for the listview 449 // use a UiModel for the listview
442 UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); 450 UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1);
443 args->model = model; 451 args->model = model;
444 452
462 g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), listview->columns); 470 g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), listview->columns);
463 471
464 GtkSelectionModel *selection_model = create_selection_model(listview, ls, args->multiselection); 472 GtkSelectionModel *selection_model = create_selection_model(listview, ls, args->multiselection);
465 GtkWidget *view = gtk_list_view_new(GTK_SELECTION_MODEL(selection_model), factory); 473 GtkWidget *view = gtk_list_view_new(GTK_SELECTION_MODEL(selection_model), factory);
466 474
467 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); 475 UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
468 476
469 // init listview 477 // init listview
470 listview->widget = view; 478 listview->widget = view;
471 listview->var = var; 479 listview->var = var;
472 listview->liststore = ls; 480 listview->liststore = ls;
511 GTK_SCROLLED_WINDOW(scroll_area), 519 GTK_SCROLLED_WINDOW(scroll_area),
512 GTK_POLICY_AUTOMATIC, 520 GTK_POLICY_AUTOMATIC,
513 GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS 521 GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS
514 SCROLLEDWINDOW_SET_CHILD(scroll_area, view); 522 SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
515 523
516 UI_APPLY_LAYOUT2(current, args); 524 if(args->width > 0 || args->height > 0) {
517 current->container->add(current->container, scroll_area); 525 int width = args->width;
518 526 int height = args->height;
519 // ct->current should point to view, not scroll_area, to make it possible 527 if(width == 0) {
520 // to add a context menu 528 width = -1;
521 current->container->current = view; 529 }
530 if(height == 0) {
531 height = -1;
532 }
533 gtk_widget_set_size_request(scroll_area, width, height);
534 }
535
536 UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
537 UiLayout layout = UI_ARGS2LAYOUT(args);
538 ct->add(ct, scroll_area, &layout);
522 539
523 return scroll_area; 540 return scroll_area;
524 } 541 }
525 542
526 UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) { 543 UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) {
527 UiObject* current = uic_current_obj(obj);
528
529 // to simplify things and share code with ui_tableview_create, we also 544 // to simplify things and share code with ui_tableview_create, we also
530 // use a UiModel for the listview 545 // use a UiModel for the listview
531 UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); 546 UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1);
532 args->model = model; 547 args->model = model;
533 548
551 g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), listview->columns); 566 g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), listview->columns);
552 g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), listview->columns); 567 g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), listview->columns);
553 568
554 GtkWidget *view = gtk_drop_down_new(G_LIST_MODEL(ls), NULL); 569 GtkWidget *view = gtk_drop_down_new(G_LIST_MODEL(ls), NULL);
555 gtk_drop_down_set_factory(GTK_DROP_DOWN(view), factory); 570 gtk_drop_down_set_factory(GTK_DROP_DOWN(view), factory);
556 571 if(args->width > 0) {
557 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); 572 gtk_widget_set_size_request(view, args->width, -1);
573 }
574
575 UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
558 576
559 // init listview 577 // init listview
560 listview->widget = view; 578 listview->widget = view;
561 listview->var = var; 579 listview->var = var;
562 listview->liststore = ls; 580 listview->liststore = ls;
587 if(args->onactivate) { 605 if(args->onactivate) {
588 g_signal_connect(view, "notify::selected", G_CALLBACK(ui_dropdown_notify), listview); 606 g_signal_connect(view, "notify::selected", G_CALLBACK(ui_dropdown_notify), listview);
589 } 607 }
590 608
591 // add widget to parent 609 // add widget to parent
592 UI_APPLY_LAYOUT2(current, args); 610 UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
593 current->container->add(current->container, view); 611 UiLayout layout = UI_ARGS2LAYOUT(args);
612 ct->add(ct, view, &layout);
613
594 return view; 614 return view;
595 } 615 }
596 616
597 void ui_listview_select(UIWIDGET listview, int index) { 617 void ui_listview_select(UIWIDGET listview, int index) {
598 GtkSelectionModel *model = gtk_list_view_get_model(GTK_LIST_VIEW(listview)); 618 GtkSelectionModel *model = gtk_list_view_get_model(GTK_LIST_VIEW(listview));
602 void ui_combobox_select(UIWIDGET dropdown, int index) { 622 void ui_combobox_select(UIWIDGET dropdown, int index) {
603 gtk_drop_down_set_selected(GTK_DROP_DOWN(dropdown), index); 623 gtk_drop_down_set_selected(GTK_DROP_DOWN(dropdown), index);
604 } 624 }
605 625
606 UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { 626 UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
607 UiObject* current = uic_current_obj(obj);
608
609 GListStore *ls = g_list_store_new(G_TYPE_OBJECT); 627 GListStore *ls = g_list_store_new(G_TYPE_OBJECT);
610 //g_list_store_append(ls, v1); 628 //g_list_store_append(ls, v1);
611 629
612 // create obj to store all relevant data we need for handling events 630 // create obj to store all relevant data we need for handling events
613 // and list updates 631 // and list updates
614 UiListView *tableview = create_listview(obj, args); 632 UiListView *tableview = create_listview(obj, args);
615 633
616 GtkSelectionModel *selection_model = create_selection_model(tableview, ls, args->multiselection); 634 GtkSelectionModel *selection_model = create_selection_model(tableview, ls, args->multiselection);
617 GtkWidget *view = gtk_column_view_new(GTK_SELECTION_MODEL(selection_model)); 635 GtkWidget *view = gtk_column_view_new(GTK_SELECTION_MODEL(selection_model));
618 636
619 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); 637 UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
620 638
621 // init tableview 639 // init tableview
622 tableview->widget = view; 640 tableview->widget = view;
623 tableview->var = var; 641 tableview->var = var;
624 tableview->liststore = ls; 642 tableview->liststore = ls;
695 GTK_SCROLLED_WINDOW(scroll_area), 713 GTK_SCROLLED_WINDOW(scroll_area),
696 GTK_POLICY_AUTOMATIC, 714 GTK_POLICY_AUTOMATIC,
697 GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS 715 GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS
698 SCROLLEDWINDOW_SET_CHILD(scroll_area, view); 716 SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
699 717
700 UI_APPLY_LAYOUT2(current, args); 718 if(args->width > 0 || args->height > 0) {
701 current->container->add(current->container, scroll_area); 719 int width = args->width;
702 720 int height = args->height;
703 // ct->current should point to view, not scroll_area, to make it possible 721 if(width == 0) {
704 // to add a context menu 722 width = -1;
705 current->container->current = view; 723 }
724 if(height == 0) {
725 height = -1;
726 }
727 gtk_widget_set_size_request(scroll_area, width, height);
728 }
729
730 UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
731 UiLayout layout = UI_ARGS2LAYOUT(args);
732 ct->add(ct, scroll_area, &layout);
706 733
707 return scroll_area; 734 return scroll_area;
708 } 735 }
709 736
710 static UiListSelection selectionmodel_get_selection(GtkSelectionModel *model) { 737 static UiListSelection selectionmodel_get_selection(GtkSelectionModel *model) {
1124 return store; 1151 return store;
1125 } 1152 }
1126 1153
1127 1154
1128 UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) { 1155 UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) {
1129 UiObject* current = uic_current_obj(obj);
1130
1131 // create treeview 1156 // create treeview
1132 GtkWidget *view = gtk_tree_view_new(); 1157 GtkWidget *view = gtk_tree_view_new();
1133 ui_set_name_and_style(view, args->name, args->style_class); 1158 ui_set_name_and_style(view, args->name, args->style_class);
1134 ui_set_widget_groups(obj->ctx, view, args->groups); 1159 ui_set_widget_groups(obj->ctx, view, args->groups);
1135 GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); 1160 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
1159 view, 1184 view,
1160 "destroy", 1185 "destroy",
1161 G_CALLBACK(ui_listview_destroy), 1186 G_CALLBACK(ui_listview_destroy),
1162 listview); 1187 listview);
1163 1188
1164 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); 1189 UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
1165 1190
1166 // init listview 1191 // init listview
1167 listview->widget = view; 1192 listview->widget = view;
1168 listview->var = var; 1193 listview->var = var;
1169 1194
1219 GTK_SCROLLED_WINDOW(scroll_area), 1244 GTK_SCROLLED_WINDOW(scroll_area),
1220 GTK_POLICY_AUTOMATIC, 1245 GTK_POLICY_AUTOMATIC,
1221 GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS 1246 GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS
1222 SCROLLEDWINDOW_SET_CHILD(scroll_area, view); 1247 SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
1223 1248
1224 UI_APPLY_LAYOUT2(current, args); 1249 if(args->width > 0 || args->height > 0) {
1225 current->container->add(current->container, scroll_area); 1250 int width = args->width;
1226 1251 int height = args->height;
1227 // ct->current should point to view, not scroll_area, to make it possible 1252 if(width == 0) {
1228 // to add a context menu 1253 width = -1;
1229 current->container->current = view; 1254 }
1255 if(height == 0) {
1256 height = -1;
1257 }
1258 gtk_widget_set_size_request(scroll_area, width, height);
1259 }
1260
1261 UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
1262 UiLayout layout = UI_ARGS2LAYOUT(args);
1263 ct->add(ct, scroll_area, &layout);
1230 1264
1231 return scroll_area; 1265 return scroll_area;
1232 } 1266 }
1233 1267
1234 void ui_listview_select(UIWIDGET listview, int index) { 1268 void ui_listview_select(UIWIDGET listview, int index) {
1241 void ui_combobox_select(UIWIDGET dropdown, int index) { 1275 void ui_combobox_select(UIWIDGET dropdown, int index) {
1242 gtk_combo_box_set_active(GTK_COMBO_BOX(dropdown), index); 1276 gtk_combo_box_set_active(GTK_COMBO_BOX(dropdown), index);
1243 } 1277 }
1244 1278
1245 UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { 1279 UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
1246 UiObject* current = uic_current_obj(obj);
1247
1248 // create treeview 1280 // create treeview
1249 GtkWidget *view = gtk_tree_view_new(); 1281 GtkWidget *view = gtk_tree_view_new();
1250 1282
1251 UiModel *model = args->model; 1283 UiModel *model = args->model;
1252 int columns = model ? model->columns : 0; 1284 int columns = model ? model->columns : 0;
1341 //gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(view), TRUE); 1373 //gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(view), TRUE);
1342 #else 1374 #else
1343 1375
1344 #endif 1376 #endif
1345 1377
1346 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); 1378 UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
1347 1379
1348 //g_signal_connect(view, "drag-begin", G_CALLBACK(drag_begin), NULL); 1380 //g_signal_connect(view, "drag-begin", G_CALLBACK(drag_begin), NULL);
1349 //g_signal_connect(view, "drag-end", G_CALLBACK(drag_end), NULL); 1381 //g_signal_connect(view, "drag-end", G_CALLBACK(drag_end), NULL);
1350 1382
1351 // add TreeView as observer to the UiList to update the TreeView if the 1383 // add TreeView as observer to the UiList to update the TreeView if the
1414 GTK_SCROLLED_WINDOW(scroll_area), 1446 GTK_SCROLLED_WINDOW(scroll_area),
1415 GTK_POLICY_AUTOMATIC, 1447 GTK_POLICY_AUTOMATIC,
1416 GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS 1448 GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS
1417 SCROLLEDWINDOW_SET_CHILD(scroll_area, view); 1449 SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
1418 1450
1451 if(args->width > 0 || args->height > 0) {
1452 int width = args->width;
1453 int height = args->height;
1454 if(width == 0) {
1455 width = -1;
1456 }
1457 if(height == 0) {
1458 height = -1;
1459 }
1460 gtk_widget_set_size_request(scroll_area, width, height);
1461 }
1462
1419 if(args->contextmenu) { 1463 if(args->contextmenu) {
1420 UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, scroll_area); 1464 UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, scroll_area);
1421 #if GTK_MAJOR_VERSION >= 4 1465 #if GTK_MAJOR_VERSION >= 4
1422 ui_widget_set_contextmenu(scroll_area, menu); 1466 ui_widget_set_contextmenu(scroll_area, menu);
1423 #else 1467 #else
1424 ui_widget_set_contextmenu(view, menu); 1468 ui_widget_set_contextmenu(view, menu);
1425 #endif 1469 #endif
1426 } 1470 }
1427 1471
1428 UI_APPLY_LAYOUT2(current, args); 1472 UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
1429 current->container->add(current->container, scroll_area); 1473 UiLayout layout = UI_ARGS2LAYOUT(args);
1430 1474 ct->add(ct, scroll_area, &layout);
1431 // ct->current should point to view, not scroll_area, to make it possible
1432 // to add a context menu
1433 current->container->current = view;
1434 1475
1435 return scroll_area; 1476 return scroll_area;
1436 } 1477 }
1437 1478
1438 1479
1474 1515
1475 1516
1476 /* --------------------------- ComboBox --------------------------- */ 1517 /* --------------------------- ComboBox --------------------------- */
1477 1518
1478 UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) { 1519 UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) {
1479 UiObject* current = uic_current_obj(obj);
1480
1481 GtkWidget *combobox = gtk_combo_box_new(); 1520 GtkWidget *combobox = gtk_combo_box_new();
1521 if(args->width > 0) {
1522 gtk_widget_set_size_request(combobox, args->width, -1);
1523 }
1482 1524
1483 ui_set_name_and_style(combobox, args->name, args->style_class); 1525 ui_set_name_and_style(combobox, args->name, args->style_class);
1484 ui_set_widget_groups(obj->ctx, combobox, args->groups); 1526 ui_set_widget_groups(obj->ctx, combobox, args->groups);
1485 UI_APPLY_LAYOUT2(current, args); 1527 UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
1486 current->container->add(current->container, combobox); 1528 UiLayout layout = UI_ARGS2LAYOUT(args);
1487 current->container->current = combobox; 1529 ct->add(ct, combobox, &layout);
1488 1530
1489 UiListView *listview = create_listview(obj, args); 1531 UiListView *listview = create_listview(obj, args);
1490 listview->widget = combobox; 1532 listview->widget = combobox;
1491 listview->style_offset = 1; 1533 listview->style_offset = 1;
1492 listview->model = ui_model(obj->ctx, UI_STRING, "", -1); 1534 listview->model = ui_model(obj->ctx, UI_STRING, "", -1);
1494 combobox, 1536 combobox,
1495 "destroy", 1537 "destroy",
1496 G_CALLBACK(ui_listview_destroy), 1538 G_CALLBACK(ui_listview_destroy),
1497 listview); 1539 listview);
1498 1540
1499 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); 1541 UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
1500 UiList *list = var ? var->value : NULL; 1542 UiList *list = var ? var->value : NULL;
1501 GtkListStore *listmodel = create_list_store(listview, list); 1543 GtkListStore *listmodel = create_list_store(listview, list);
1502 if(var) { 1544 if(var) {
1503 listview->var = var; 1545 listview->var = var;
1504 list->update = ui_combobox_modelupdate; 1546 list->update = ui_combobox_modelupdate;
2019 } 2061 }
2020 2062
2021 2063
2022 /* ------------------------------ Source List ------------------------------ */ 2064 /* ------------------------------ Source List ------------------------------ */
2023 2065
2066 static ui_sourcelist_update_func sourcelist_update_finished_callback;
2067
2068 void ui_sourcelist_set_update_callback(ui_sourcelist_update_func cb) {
2069 sourcelist_update_finished_callback = cb;
2070 }
2071
2072 static void ui_sourcelist_update_finished(void) {
2073 if(sourcelist_update_finished_callback) {
2074 sourcelist_update_finished_callback();
2075 }
2076 }
2077
2024 static void ui_destroy_sourcelist(GtkWidget *w, UiListBox *v) { 2078 static void ui_destroy_sourcelist(GtkWidget *w, UiListBox *v) {
2025 cxListFree(v->sublists); 2079 cxListFree(v->sublists);
2026 free(v); 2080 free(v);
2027 } 2081 }
2028 2082
2045 } 2099 }
2046 2100
2047 if(sublist->separator) { 2101 if(sublist->separator) {
2048 GtkWidget *separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); 2102 GtkWidget *separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
2049 gtk_list_box_row_set_header(row, separator); 2103 gtk_list_box_row_set_header(row, separator);
2050 } else if(sublist->header) { 2104 } else if(sublist->header && !listbox->header_is_item) {
2051 GtkWidget *header = gtk_label_new(sublist->header); 2105 GtkWidget *header = gtk_label_new(sublist->header);
2052 gtk_widget_set_halign(header, GTK_ALIGN_START); 2106 gtk_widget_set_halign(header, GTK_ALIGN_START);
2053 if(row == listbox->first_row) { 2107 if(row == listbox->first_row) {
2054 WIDGET_ADD_CSS_CLASS(header, "ui-listbox-header-first"); 2108 WIDGET_ADD_CSS_CLASS(header, "ui-listbox-header-first");
2055 } else { 2109 } else {
2108 list->update = ui_listbox_list_update; 2162 list->update = ui_listbox_list_update;
2109 } 2163 }
2110 } 2164 }
2111 2165
2112 UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) { 2166 UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) {
2113 UiObject* current = uic_current_obj(obj);
2114
2115 #ifdef UI_GTK3 2167 #ifdef UI_GTK3
2116 GtkWidget *listbox = g_object_new(ui_sidebar_list_box_get_type(), NULL); 2168 GtkWidget *listbox = g_object_new(ui_sidebar_list_box_get_type(), NULL);
2117 #else 2169 #else
2118 GtkWidget *listbox = gtk_list_box_new(); 2170 GtkWidget *listbox = gtk_list_box_new();
2119 #endif 2171 #endif
2128 GtkWidget *scroll_area = SCROLLEDWINDOW_NEW(); 2180 GtkWidget *scroll_area = SCROLLEDWINDOW_NEW();
2129 SCROLLEDWINDOW_SET_CHILD(scroll_area, listbox); 2181 SCROLLEDWINDOW_SET_CHILD(scroll_area, listbox);
2130 2182
2131 ui_set_name_and_style(listbox, args->name, args->style_class); 2183 ui_set_name_and_style(listbox, args->name, args->style_class);
2132 ui_set_widget_groups(obj->ctx, listbox, args->groups); 2184 ui_set_widget_groups(obj->ctx, listbox, args->groups);
2133 UI_APPLY_LAYOUT2(current, args); 2185 UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
2134 current->container->add(current->container, scroll_area); 2186 UiLayout layout = UI_ARGS2LAYOUT(args);
2187 ct->add(ct, scroll_area, &layout);
2135 2188
2136 UiListBox *uilistbox = malloc(sizeof(UiListBox)); 2189 UiListBox *uilistbox = malloc(sizeof(UiListBox));
2137 uilistbox->obj = obj; 2190 uilistbox->obj = obj;
2138 uilistbox->listbox = GTK_LIST_BOX(listbox); 2191 uilistbox->listbox = GTK_LIST_BOX(listbox);
2192 uilistbox->header_is_item = args->header_is_item;
2139 uilistbox->getvalue = args->getvalue; 2193 uilistbox->getvalue = args->getvalue;
2140 uilistbox->getvaluedata = args->getvaluedata; 2194 uilistbox->getvaluedata = args->getvaluedata;
2141 uilistbox->onactivate = args->onactivate; 2195 uilistbox->onactivate = args->onactivate;
2142 uilistbox->onactivatedata = args->onactivatedata; 2196 uilistbox->onactivatedata = args->onactivatedata;
2143 uilistbox->onbuttonclick = args->onbuttonclick; 2197 uilistbox->onbuttonclick = args->onbuttonclick;
2162 } 2216 }
2163 2217
2164 // fill items 2218 // fill items
2165 ui_listbox_update(uilistbox, 0, cxListSize(uilistbox->sublists)); 2219 ui_listbox_update(uilistbox, 0, cxListSize(uilistbox->sublists));
2166 } else { 2220 } else {
2167 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->dynamic_sublist, args->varname, UI_VAR_LIST); 2221 UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->dynamic_sublist, args->varname, UI_VAR_LIST);
2168 if(var) { 2222 if(var) {
2169 UiList *list = var->value; 2223 UiList *list = var->value;
2170 list->obj = uilistbox; 2224 list->obj = uilistbox;
2171 list->update = ui_listbox_dynamic_update; 2225 list->update = ui_listbox_dynamic_update;
2172 2226
2255 // reload sublist 2309 // reload sublist
2256 sublist->startpos = pos; 2310 sublist->startpos = pos;
2257 ui_listbox_update_sublist(listbox, sublist, pos); 2311 ui_listbox_update_sublist(listbox, sublist, pos);
2258 pos += sublist->numitems; 2312 pos += sublist->numitems;
2259 } 2313 }
2260 } 2314
2261 2315 ui_sourcelist_update_finished();
2262 static void listbox_button_clicked(GtkWidget *widget, UiEventDataExt *data) { 2316 }
2317
2318 static void listbox_button_clicked(GtkWidget *button, UiEventDataExt *data) {
2263 UiListBoxSubList *sublist = data->customdata0; 2319 UiListBoxSubList *sublist = data->customdata0;
2264 2320
2265 UiSubListEventData eventdata; 2321 UiSubListEventData eventdata;
2266 eventdata.list = sublist->var->value; 2322 eventdata.list = sublist->var->value;
2267 eventdata.sublist_index = sublist->index; 2323 eventdata.sublist_index = sublist->index;
2280 event.set = ui_get_setop(); 2336 event.set = ui_get_setop();
2281 2337
2282 if(data->callback2) { 2338 if(data->callback2) {
2283 data->callback2(&event, data->userdata2); 2339 data->callback2(&event, data->userdata2);
2284 } 2340 }
2285 } 2341
2342 if(data->customdata3) {
2343 UIMENU menu = data->customdata3;
2344 g_object_set_data(G_OBJECT(button), "ui-button-popup", menu);
2345 gtk_popover_popup(GTK_POPOVER(menu));
2346 }
2347 }
2348
2349 #if GTK_CHECK_VERSION(3, 0, 0)
2350 static void button_popover_closed(GtkPopover *popover, GtkWidget *button) {
2351 g_object_set_data(G_OBJECT(button), "ui-button-popup", NULL);
2352 if(g_object_get_data(G_OBJECT(button), "ui-button-invisible")) {
2353 g_object_set_data(G_OBJECT(button), "ui-button-invisible", NULL);
2354 gtk_widget_set_visible(button, FALSE);
2355 }
2356 }
2357 #endif
2286 2358
2287 static void listbox_fill_row(UiListBox *listbox, GtkWidget *row, UiListBoxSubList *sublist, UiSubListItem *item, int index) { 2359 static void listbox_fill_row(UiListBox *listbox, GtkWidget *row, UiListBoxSubList *sublist, UiSubListItem *item, int index) {
2360 UiBool is_header = index < 0;
2361
2288 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10); 2362 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
2289 if(item->icon) { 2363 if(item->icon) {
2290 GtkWidget *icon = ICON_IMAGE(item->icon); 2364 GtkWidget *icon = ICON_IMAGE(item->icon);
2291 BOX_ADD(hbox, icon); 2365 BOX_ADD(hbox, icon);
2292 } 2366 }
2293 GtkWidget *label = gtk_label_new(item->label); 2367 GtkWidget *label = gtk_label_new(item->label);
2368 if(is_header) {
2369 WIDGET_ADD_CSS_CLASS(label, "ui-listbox-header-row");
2370 }
2294 gtk_widget_set_halign(label, GTK_ALIGN_START); 2371 gtk_widget_set_halign(label, GTK_ALIGN_START);
2295 BOX_ADD_EXPAND(hbox, label); 2372 BOX_ADD_EXPAND(hbox, label);
2296 if(item->badge) { 2373 if(item->badge) {
2297 2374
2298 } 2375 }
2308 event->callback = listbox->onactivate; 2385 event->callback = listbox->onactivate;
2309 event->userdata = listbox->onactivatedata; 2386 event->userdata = listbox->onactivatedata;
2310 event->callback2 = listbox->onbuttonclick; 2387 event->callback2 = listbox->onbuttonclick;
2311 event->userdata2 = listbox->onbuttonclickdata; 2388 event->userdata2 = listbox->onbuttonclickdata;
2312 event->value0 = index; 2389 event->value0 = index;
2390
2391 // TODO: semi-memory leak when listbox_fill_row is called again for the same row
2392 // each row update will create a new UiEventDataExt object and a separate destroy handler
2313 2393
2314 g_signal_connect( 2394 g_signal_connect(
2315 row, 2395 row,
2316 "destroy", 2396 "destroy",
2317 G_CALLBACK(ui_destroy_userdata), 2397 G_CALLBACK(ui_destroy_userdata),
2343 button, 2423 button,
2344 "clicked", 2424 "clicked",
2345 G_CALLBACK(listbox_button_clicked), 2425 G_CALLBACK(listbox_button_clicked),
2346 event 2426 event
2347 ); 2427 );
2428 gtk_widget_set_visible(button, FALSE);
2429
2430 g_object_set_data(G_OBJECT(row), "ui-listbox-row-button", button);
2431
2432 // menu
2433 if(item->button_menu) {
2434 UIMENU menu = ui_contextmenu_create(item->button_menu, listbox->obj, button);
2435 event->customdata3 = menu;
2436 g_signal_connect(menu, "closed", G_CALLBACK(button_popover_closed), button);
2437 ui_menubuilder_unref(item->button_menu);
2438 }
2348 } 2439 }
2349 } 2440 }
2350 2441
2351 static void update_sublist_item(UiListBox *listbox, UiListBoxSubList *sublist, int index) { 2442 static void update_sublist_item(UiListBox *listbox, UiListBoxSubList *sublist, int index) {
2352 GtkListBoxRow *row = gtk_list_box_get_row_at_index(listbox->listbox, sublist->startpos + index); 2443 int header_row = listbox->header_is_item && sublist->header ? 1 : 0;
2444 GtkListBoxRow *row = gtk_list_box_get_row_at_index(listbox->listbox, sublist->startpos + index + header_row);
2353 if(!row) { 2445 if(!row) {
2354 return; 2446 return;
2355 } 2447 }
2356 UiList *list = sublist->var->value; 2448 UiList *list = sublist->var->value;
2357 if(!list) { 2449 if(!list) {
2376 free(item.button_label); 2468 free(item.button_label);
2377 free(item.button_icon); 2469 free(item.button_icon);
2378 free(item.badge); 2470 free(item.badge);
2379 } 2471 }
2380 2472
2473 static void listbox_row_on_enter(GtkWidget *row) {
2474 GtkWidget *button = g_object_get_data(G_OBJECT(row), "ui-listbox-row-button");
2475 if(button) {
2476 gtk_widget_set_visible(button, TRUE);
2477 }
2478 }
2479
2480 static void listbox_row_on_leave(GtkWidget *row) {
2481 GtkWidget *button = g_object_get_data(G_OBJECT(row), "ui-listbox-row-button");
2482 if(button) {
2483 if(!g_object_get_data(G_OBJECT(button), "ui-button-popup")) {
2484 gtk_widget_set_visible(button, FALSE);
2485 } else {
2486 g_object_set_data(G_OBJECT(button), "ui-button-invisible", (void*)1);
2487 }
2488 }
2489 }
2490
2491 #if GTK_CHECK_VERSION(4, 0, 0)
2492 static void listbox_row_enter(
2493 GtkEventControllerMotion* self,
2494 gdouble x,
2495 gdouble y,
2496 GtkWidget *row)
2497 {
2498 listbox_row_on_enter(row);
2499 }
2500
2501 static void listbox_row_leave(
2502 GtkEventControllerMotion* self,
2503 GtkWidget *row)
2504 {
2505 listbox_row_on_leave(row);
2506 }
2507 #else
2508 static gboolean listbox_row_enter(
2509 GtkWidget *row,
2510 GdkEventCrossing event,
2511 gpointer user_data)
2512 {
2513 listbox_row_on_enter(row);
2514 return FALSE;
2515 }
2516
2517
2518 static gboolean listbox_row_leave(
2519 GtkWidget *row,
2520 GdkEventCrossing *event,
2521 gpointer user_data)
2522 {
2523 listbox_row_on_leave(row);
2524 return FALSE;
2525 }
2526
2527 #endif
2528
2381 void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, size_t listbox_insert_index) { 2529 void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, size_t listbox_insert_index) {
2382 // clear sublist 2530 // clear sublist
2383 CxIterator r = cxListIterator(sublist->widgets); 2531 CxIterator r = cxListIterator(sublist->widgets);
2384 cx_foreach(GtkWidget*, widget, r) { 2532 cx_foreach(GtkWidget*, widget, r) {
2385 LISTBOX_REMOVE(listbox->listbox, widget); 2533 LISTBOX_REMOVE(listbox->listbox, widget);
2395 UiList *list = sublist->var->value; 2543 UiList *list = sublist->var->value;
2396 if(!list) { 2544 if(!list) {
2397 return; 2545 return;
2398 } 2546 }
2399 2547
2400 size_t index = 0; 2548 int index = 0;
2401 void *elm = list->first(list); 2549 void *elm = list->first(list);
2402 2550 void *first = elm;
2403 if(!elm && sublist->header) { 2551
2552 if(sublist->header && !listbox->header_is_item && !elm) {
2404 // empty row for header 2553 // empty row for header
2405 GtkWidget *row = gtk_list_box_row_new(); 2554 GtkWidget *row = gtk_list_box_row_new();
2406 cxListAdd(sublist->widgets, row); 2555 cxListAdd(sublist->widgets, row);
2407 g_object_set_data(G_OBJECT(row), "ui_listbox", listbox); 2556 g_object_set_data(G_OBJECT(row), "ui_listbox", listbox);
2408 g_object_set_data(G_OBJECT(row), "ui_listbox_sublist", sublist); 2557 g_object_set_data(G_OBJECT(row), "ui_listbox_sublist", sublist);
2409 intptr_t rowindex = listbox_insert_index + index; 2558 //intptr_t rowindex = listbox_insert_index + index;
2410 g_object_set_data(G_OBJECT(row), "ui_listbox_row_index", (gpointer)rowindex); 2559 //g_object_set_data(G_OBJECT(row), "ui_listbox_row_index", (gpointer)rowindex);
2411 gtk_list_box_insert(listbox->listbox, row, listbox_insert_index + index); 2560 gtk_list_box_insert(listbox->listbox, row, listbox_insert_index + index);
2412 sublist->numitems = 1; 2561 sublist->numitems = 1;
2413 return; 2562 return;
2563 }
2564
2565 int first_index = 0;
2566 int header_row = 0;
2567 if(listbox->header_is_item && sublist->header) {
2568 index = -1;
2569 first_index = -1;
2570 header_row = 1;
2571 elm = sublist->header;
2414 } 2572 }
2415 2573
2416 while(elm) { 2574 while(elm) {
2417 UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL }; 2575 UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL };
2418 if(listbox->getvalue) { 2576 if(listbox->getvalue) {
2419 listbox->getvalue(list, sublist->userdata, elm, index, &item, listbox->getvaluedata); 2577 listbox->getvalue(list, sublist->userdata, elm, index, &item, listbox->getvaluedata);
2420 } else { 2578 } else {
2421 item.label = strdup(elm); 2579 item.label = strdup(elm);
2422 } 2580 }
2423 2581
2582 if(item.label == NULL && index == -1 && sublist->header) {
2583 item.label = strdup(sublist->header);
2584 }
2585
2424 // create listbox item 2586 // create listbox item
2425 GtkWidget *row = gtk_list_box_row_new(); 2587 GtkWidget *row = gtk_list_box_row_new();
2426 listbox_fill_row(listbox, row, sublist, &item, (int)index); 2588 #if GTK_CHECK_VERSION(4, 0, 0)
2427 if(index == 0) { 2589 GtkEventController *motion_controller = gtk_event_controller_motion_new();
2590 gtk_widget_add_controller(GTK_WIDGET(row), motion_controller);
2591 g_signal_connect(motion_controller, "enter", G_CALLBACK(listbox_row_enter), row);
2592 g_signal_connect(motion_controller, "leave", G_CALLBACK(listbox_row_leave), row);
2593 #else
2594 gtk_widget_set_events(GTK_WIDGET(row), GDK_POINTER_MOTION_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
2595 g_signal_connect(row, "enter-notify-event", G_CALLBACK(listbox_row_enter), NULL);
2596 g_signal_connect(row, "leave-notify-event", G_CALLBACK(listbox_row_leave), NULL);
2597 #endif
2598
2599 listbox_fill_row(listbox, row, sublist, &item, index);
2600 if(index == first_index) {
2428 // first row in the sublist, set ui_listbox data to the row 2601 // first row in the sublist, set ui_listbox data to the row
2429 // which is then used by the headerfunc 2602 // which is then used by the headerfunc
2430 g_object_set_data(G_OBJECT(row), "ui_listbox", listbox); 2603 g_object_set_data(G_OBJECT(row), "ui_listbox", listbox);
2431 g_object_set_data(G_OBJECT(row), "ui_listbox_sublist", sublist); 2604 g_object_set_data(G_OBJECT(row), "ui_listbox_sublist", sublist);
2432 2605
2433 if(listbox_insert_index == 0) { 2606 if(listbox_insert_index == 0) {
2434 // first row in the GtkListBox 2607 // first row in the GtkListBox
2435 listbox->first_row = GTK_LIST_BOX_ROW(row); 2608 listbox->first_row = GTK_LIST_BOX_ROW(row);
2436 } 2609 }
2437 } 2610 }
2438 intptr_t rowindex = listbox_insert_index + index; 2611 //intptr_t rowindex = listbox_insert_index + index;
2439 g_object_set_data(G_OBJECT(row), "ui_listbox_row_index", (gpointer)rowindex); 2612 //g_object_set_data(G_OBJECT(row), "ui_listbox_row_index", (gpointer)rowindex);
2440 gtk_list_box_insert(listbox->listbox, row, listbox_insert_index + index); 2613 gtk_list_box_insert(listbox->listbox, row, listbox_insert_index + index + header_row);
2441 cxListAdd(sublist->widgets, row); 2614 cxListAdd(sublist->widgets, row);
2442 2615
2443 // cleanup 2616 // cleanup
2444 free(item.label); 2617 free(item.label);
2445 free(item.icon); 2618 free(item.icon);
2446 free(item.button_label); 2619 free(item.button_label);
2447 free(item.button_icon); 2620 free(item.button_icon);
2448 free(item.badge); 2621 free(item.badge);
2449 2622
2450 // next row 2623 // next row
2451 elm = list->next(list); 2624 elm = index >= 0 ? list->next(list) : first;
2452 index++; 2625 index++;
2453 } 2626 }
2454 2627
2455 sublist->numitems = cxListSize(sublist->widgets); 2628 sublist->numitems = cxListSize(sublist->widgets);
2456 } 2629 }
2466 pos += ls->numitems; 2639 pos += ls->numitems;
2467 } 2640 }
2468 } else { 2641 } else {
2469 update_sublist_item(sublist->listbox, sublist, i); 2642 update_sublist_item(sublist->listbox, sublist, i);
2470 } 2643 }
2644
2645 ui_sourcelist_update_finished();
2471 } 2646 }
2472 2647
2473 void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) { 2648 void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) {
2474 UiEventDataExt *data = g_object_get_data(G_OBJECT(row), "ui-listbox-row-eventdata"); 2649 UiEventDataExt *data = g_object_get_data(G_OBJECT(row), "ui-listbox-row-eventdata");
2475 if(!data) { 2650 if(!data) {
2480 UiSubListEventData eventdata; 2655 UiSubListEventData eventdata;
2481 eventdata.list = sublist->var->value; 2656 eventdata.list = sublist->var->value;
2482 eventdata.sublist_index = sublist->index; 2657 eventdata.sublist_index = sublist->index;
2483 eventdata.row_index = data->value0; 2658 eventdata.row_index = data->value0;
2484 eventdata.sublist_userdata = sublist->userdata; 2659 eventdata.sublist_userdata = sublist->userdata;
2485 eventdata.row_data = eventdata.list->get(eventdata.list, eventdata.row_index); 2660 eventdata.row_data = eventdata.row_index >= 0 ? eventdata.list->get(eventdata.list, eventdata.row_index) : NULL;
2486 eventdata.event_data = data->customdata2; 2661 eventdata.event_data = data->customdata2;
2487 2662
2488 UiEvent event; 2663 UiEvent event;
2489 event.obj = data->obj; 2664 event.obj = data->obj;
2490 event.window = event.obj->window; 2665 event.window = event.obj->window;

mercurial