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 } |