ui/gtk/list.c

branch
newapi
changeset 411
3e91b7aff6a1
parent 401
55fb6bad549d
child 413
b8e41d42f400
equal deleted inserted replaced
410:a080c69125d1 411:3e91b7aff6a1
34 #include "../common/context.h" 34 #include "../common/context.h"
35 #include "../common/object.h" 35 #include "../common/object.h"
36 #include "container.h" 36 #include "container.h"
37 37
38 #include <cx/array_list.h> 38 #include <cx/array_list.h>
39 #include <cx/linked_list.h>
39 40
40 #include "list.h" 41 #include "list.h"
41 #include "icon.h" 42 #include "icon.h"
42 #include "menu.h" 43 #include "menu.h"
43 #include "dnd.h" 44 #include "dnd.h"
980 UiListView *combobox = list->obj; 981 UiListView *combobox = list->obj;
981 if(selection.count > 0) { 982 if(selection.count > 0) {
982 gtk_combo_box_set_active(GTK_COMBO_BOX(combobox->widget), selection.rows[0]); 983 gtk_combo_box_set_active(GTK_COMBO_BOX(combobox->widget), selection.rows[0]);
983 } 984 }
984 } 985 }
986
987
988 /* ------------------------------ Source List ------------------------------ */
989
990 static void ui_destroy_sourcelist(GtkWidget *w, UiListBox *v) {
991 cxListDestroy(v->sublists);
992 free(v);
993 }
994
995 static void sublist_destroy(UiObject *obj, UiListBoxSubList *sublist) {
996 free(sublist->header);
997 ui_destroy_boundvar(obj->ctx, sublist->var);
998 cxListDestroy(sublist->widgets);
999 }
1000
1001 static void listbox_create_header(GtkListBoxRow* row, GtkListBoxRow* before, gpointer user_data) {
1002 // first rows in sublists have the ui_listbox property
1003 UiListBox *listbox = g_object_get_data(G_OBJECT(row), "ui_listbox");
1004 if(!listbox) {
1005 return;
1006 }
1007
1008 UiListBoxSubList *sublist = g_object_get_data(G_OBJECT(row), "ui_listbox_sublist");
1009 if(!sublist) {
1010 return;
1011 }
1012
1013 if(sublist->separator) {
1014 GtkWidget *separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
1015 gtk_list_box_row_set_header(row, separator);
1016 } else if(sublist->header) {
1017 GtkWidget *header = gtk_label_new(sublist->header);
1018 gtk_widget_set_halign(header, GTK_ALIGN_START);
1019 if(row == listbox->first_row) {
1020 WIDGET_ADD_CSS_CLASS(header, "ui-listbox-header-first");
1021 } else {
1022 WIDGET_ADD_CSS_CLASS(header, "ui-listbox-header");
1023 }
1024 gtk_list_box_row_set_header(row, header);
1025 }
1026 }
1027
1028 UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs args) {
1029 UiObject* current = uic_current_obj(obj);
1030
1031 GtkWidget *listbox = gtk_list_box_new();
1032 if(!args.style_class) {
1033 WIDGET_ADD_CSS_CLASS(listbox, "navigation-sidebar");
1034 }
1035 gtk_list_box_set_header_func(GTK_LIST_BOX(listbox), listbox_create_header, NULL, NULL);
1036 GtkWidget *scroll_area = SCROLLEDWINDOW_NEW();
1037 SCROLLEDWINDOW_SET_CHILD(scroll_area, listbox);
1038
1039 ui_set_name_and_style(listbox, args.name, args.style_class);
1040 ui_set_widget_groups(obj->ctx, listbox, args.groups);
1041 UI_APPLY_LAYOUT1(current, args);
1042 current->container->add(current->container, scroll_area, TRUE);
1043
1044 UiListBox *uilistbox = malloc(sizeof(UiListBox));
1045 uilistbox->obj = obj;
1046 uilistbox->listbox = GTK_LIST_BOX(listbox);
1047 uilistbox->getvalue = args.getvalue;
1048 uilistbox->onactivate = args.onactivate;
1049 uilistbox->onactivatedata = args.onactivatedata;
1050 uilistbox->onbuttonclick = args.onbuttonclick;
1051 uilistbox->onbuttonclickdata = args.onbuttonclickdata;
1052 uilistbox->sublists = cxArrayListCreateSimple(sizeof(UiListBoxSubList), 4);
1053 uilistbox->sublists->collection.advanced_destructor = (cx_destructor_func2)sublist_destroy;
1054 uilistbox->sublists->collection.destructor_data = obj;
1055 uilistbox->first_row = NULL;
1056
1057 if(args.numsublists == 0 && args.sublists) {
1058 args.numsublists = INT_MAX;
1059 }
1060 for(int i=0;i<args.numsublists;i++) {
1061 UiSubList sublist = args.sublists[i];
1062 if(!sublist.varname && !sublist.value) {
1063 break;
1064 }
1065
1066 UiListBoxSubList uisublist;
1067 uisublist.var = uic_widget_var(
1068 obj->ctx,
1069 current->ctx,
1070 sublist.value,
1071 sublist.varname,
1072 UI_VAR_LIST);
1073 uisublist.numitems = 0;
1074 uisublist.header = sublist.header ? strdup(sublist.header) : NULL;
1075 uisublist.separator = sublist.separator;
1076 uisublist.widgets = cxLinkedListCreateSimple(CX_STORE_POINTERS);
1077 uisublist.listbox = uilistbox;
1078 uisublist.userdata = sublist.userdata;
1079 uisublist.index = i;
1080
1081 cxListAdd(uilistbox->sublists, &uisublist);
1082
1083 // bind UiList
1084 UiListBoxSubList *sublist_ptr = cxListAt(uilistbox->sublists, cxListSize(uilistbox->sublists)-1);
1085 UiList *list = uisublist.var->value;
1086 if(list) {
1087 list->obj = sublist_ptr;
1088 list->update = ui_listbox_list_update;
1089 }
1090 }
1091 // fill items
1092 ui_listbox_update(uilistbox, 0, cxListSize(uilistbox->sublists));
1093
1094 // register uilistbox for both widgets, so it doesn't matter which
1095 // widget is used later
1096 g_object_set_data(G_OBJECT(scroll_area), "ui_listbox", uilistbox);
1097 g_object_set_data(G_OBJECT(listbox), "ui_listbox", uilistbox);
1098
1099 // signals
1100 g_signal_connect(
1101 listbox,
1102 "destroy",
1103 G_CALLBACK(ui_destroy_sourcelist),
1104 uilistbox);
1105
1106 if(args.onactivate) {
1107 g_signal_connect(
1108 listbox,
1109 "row-activated",
1110 G_CALLBACK(ui_listbox_row_activate),
1111 NULL);
1112 }
1113
1114 return scroll_area;
1115 }
1116
1117 void ui_listbox_update(UiListBox *listbox, int from, int to) {
1118 CxIterator i = cxListIterator(listbox->sublists);
1119 size_t pos = 0;
1120 cx_foreach(UiListBoxSubList *, sublist, i) {
1121 if(i.index < from) {
1122 pos += sublist->numitems;
1123 continue;
1124 }
1125 if(i.index > to) {
1126 break;
1127 }
1128
1129 // reload sublist
1130 ui_listbox_update_sublist(listbox, sublist, pos);
1131 pos += sublist->numitems;
1132 }
1133 }
1134
1135 static GtkWidget* create_listbox_row(UiListBox *listbox, UiListBoxSubList *sublist, UiSubListItem *item, int index) {
1136 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
1137 if(item->icon) {
1138 GtkWidget *icon = ICON_IMAGE(item->icon);
1139 BOX_ADD(hbox, icon);
1140 }
1141 GtkWidget *label = gtk_label_new(item->label);
1142 gtk_widget_set_halign(label, GTK_ALIGN_START);
1143 BOX_ADD_EXPAND(hbox, label);
1144 // TODO: badge, button
1145 GtkWidget *row = gtk_list_box_row_new();
1146 LISTBOX_ROW_SET_CHILD(row, hbox);
1147
1148 // signals
1149 UiEventDataExt *event = malloc(sizeof(UiEventDataExt));
1150 memset(event, 0, sizeof(UiEventDataExt));
1151 event->obj = listbox->obj;
1152 event->customdata0 = sublist;
1153 event->customdata1 = sublist->var;
1154 event->customdata2 = item->eventdata;
1155 event->callback = listbox->onactivate;
1156 event->userdata = listbox->onactivatedata;
1157 event->callback2 = listbox->onbuttonclick;
1158 event->userdata2 = listbox->onbuttonclickdata;
1159 event->value0 = index;
1160
1161 g_signal_connect(
1162 row,
1163 "destroy",
1164 G_CALLBACK(ui_destroy_userdata),
1165 event);
1166
1167 g_object_set_data(G_OBJECT(row), "ui-listbox-row-eventdata", event);
1168
1169 return row;
1170 }
1171
1172 void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, size_t listbox_insert_index) {
1173 // clear sublist
1174 CxIterator r = cxListIterator(sublist->widgets);
1175 cx_foreach(GtkWidget*, widget, r) {
1176 LISTBOX_REMOVE(listbox->listbox, widget);
1177 }
1178 cxListClear(sublist->widgets);
1179
1180 sublist->numitems = 0;
1181
1182 // create items for each UiList element
1183 UiList *list = sublist->var->value;
1184 if(!list) {
1185 return;
1186 }
1187
1188 size_t index = 0;
1189 void *elm = list->first(list);
1190 while(elm) {
1191 UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL };
1192 listbox->getvalue(sublist->userdata, elm, index, &item);
1193
1194 // create listbox item
1195 GtkWidget *row = create_listbox_row(listbox, sublist, &item, (int)index);
1196 if(index == 0) {
1197 // first row in the sublist, set ui_listbox data to the row
1198 // which is then used by the headerfunc
1199 g_object_set_data(G_OBJECT(row), "ui_listbox", listbox);
1200 g_object_set_data(G_OBJECT(row), "ui_listbox_sublist", sublist);
1201
1202 if(listbox_insert_index == 0) {
1203 // first row in the GtkListBox
1204 listbox->first_row = GTK_LIST_BOX_ROW(row);
1205 }
1206 }
1207 intptr_t rowindex = listbox_insert_index + index;
1208 g_object_set_data(G_OBJECT(row), "ui_listbox_row_index", (gpointer)rowindex);
1209 gtk_list_box_insert(listbox->listbox, row, listbox_insert_index + index);
1210 cxListAdd(sublist->widgets, row);
1211
1212 // cleanup
1213 free(item.label);
1214 free(item.icon);
1215 free(item.button_label);
1216 free(item.button_icon);
1217 free(item.badge);
1218
1219 // next row
1220 elm = list->next(list);
1221 index++;
1222 }
1223
1224 sublist->numitems = cxListSize(sublist->widgets);
1225 }
1226
1227 void ui_listbox_list_update(UiList *list, int i) {
1228 UiListBoxSubList *sublist = list->obj;
1229 }
1230
1231 void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) {
1232 UiEventDataExt *data = g_object_get_data(G_OBJECT(row), "ui-listbox-row-eventdata");
1233 if(!data) {
1234 return;
1235 }
1236 UiListBoxSubList *sublist = data->customdata0;
1237
1238 UiEvent event;
1239 event.obj = data->obj;
1240 event.window = event.obj->window;
1241 event.document = event.obj->ctx->document;
1242 event.eventdata = data->customdata2;
1243 event.intval = data->value0;
1244
1245 if(data->callback) {
1246 data->callback(&event, data->userdata);
1247 }
1248 }

mercurial