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 #ifdef UI_GTK3 |
|
1029 typedef struct _UiSidebarListBoxClass { |
|
1030 GtkListBoxClass parent_class; |
|
1031 } UiSidebarListBoxClass; |
|
1032 |
|
1033 typedef struct _UiSidebarListBox { |
|
1034 GtkListBox parent_instance; |
|
1035 } UiSidebarListBox; |
|
1036 |
|
1037 G_DEFINE_TYPE(UiSidebarListBox, ui_sidebar_list_box, GTK_TYPE_LIST_BOX) |
|
1038 |
|
1039 /* Initialize the instance */ |
|
1040 static void ui_sidebar_list_box_class_init(UiSidebarListBoxClass *klass) { |
|
1041 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); |
|
1042 gtk_widget_class_set_css_name (widget_class, "placessidebar"); |
|
1043 } |
|
1044 |
|
1045 static void ui_sidebar_list_box_init(UiSidebarListBox *self) { |
|
1046 |
|
1047 } |
|
1048 #endif |
|
1049 |
|
1050 UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs args) { |
|
1051 UiObject* current = uic_current_obj(obj); |
|
1052 |
|
1053 #ifdef UI_GTK3 |
|
1054 GtkWidget *listbox = g_object_new(ui_sidebar_list_box_get_type(), NULL); |
|
1055 #else |
|
1056 GtkWidget *listbox = gtk_list_box_new(); |
|
1057 #endif |
|
1058 if(!args.style_class) { |
|
1059 #if GTK_MAJOR_VERSION >= 4 |
|
1060 WIDGET_ADD_CSS_CLASS(listbox, "navigation-sidebar"); |
|
1061 #else |
|
1062 WIDGET_ADD_CSS_CLASS(listbox, "sidebar"); |
|
1063 #endif |
|
1064 } |
|
1065 gtk_list_box_set_header_func(GTK_LIST_BOX(listbox), listbox_create_header, NULL, NULL); |
|
1066 GtkWidget *scroll_area = SCROLLEDWINDOW_NEW(); |
|
1067 SCROLLEDWINDOW_SET_CHILD(scroll_area, listbox); |
|
1068 |
|
1069 ui_set_name_and_style(listbox, args.name, args.style_class); |
|
1070 ui_set_widget_groups(obj->ctx, listbox, args.groups); |
|
1071 UI_APPLY_LAYOUT1(current, args); |
|
1072 current->container->add(current->container, scroll_area, TRUE); |
|
1073 |
|
1074 UiListBox *uilistbox = malloc(sizeof(UiListBox)); |
|
1075 uilistbox->obj = obj; |
|
1076 uilistbox->listbox = GTK_LIST_BOX(listbox); |
|
1077 uilistbox->getvalue = args.getvalue; |
|
1078 uilistbox->onactivate = args.onactivate; |
|
1079 uilistbox->onactivatedata = args.onactivatedata; |
|
1080 uilistbox->onbuttonclick = args.onbuttonclick; |
|
1081 uilistbox->onbuttonclickdata = args.onbuttonclickdata; |
|
1082 uilistbox->sublists = cxArrayListCreateSimple(sizeof(UiListBoxSubList), 4); |
|
1083 uilistbox->sublists->collection.advanced_destructor = (cx_destructor_func2)sublist_destroy; |
|
1084 uilistbox->sublists->collection.destructor_data = obj; |
|
1085 uilistbox->first_row = NULL; |
|
1086 |
|
1087 if(args.numsublists == 0 && args.sublists) { |
|
1088 args.numsublists = INT_MAX; |
|
1089 } |
|
1090 for(int i=0;i<args.numsublists;i++) { |
|
1091 UiSubList sublist = args.sublists[i]; |
|
1092 if(!sublist.varname && !sublist.value) { |
|
1093 break; |
|
1094 } |
|
1095 |
|
1096 UiListBoxSubList uisublist; |
|
1097 uisublist.var = uic_widget_var( |
|
1098 obj->ctx, |
|
1099 current->ctx, |
|
1100 sublist.value, |
|
1101 sublist.varname, |
|
1102 UI_VAR_LIST); |
|
1103 uisublist.numitems = 0; |
|
1104 uisublist.header = sublist.header ? strdup(sublist.header) : NULL; |
|
1105 uisublist.separator = sublist.separator; |
|
1106 uisublist.widgets = cxLinkedListCreateSimple(CX_STORE_POINTERS); |
|
1107 uisublist.listbox = uilistbox; |
|
1108 uisublist.userdata = sublist.userdata; |
|
1109 uisublist.index = i; |
|
1110 |
|
1111 cxListAdd(uilistbox->sublists, &uisublist); |
|
1112 |
|
1113 // bind UiList |
|
1114 UiListBoxSubList *sublist_ptr = cxListAt(uilistbox->sublists, cxListSize(uilistbox->sublists)-1); |
|
1115 UiList *list = uisublist.var->value; |
|
1116 if(list) { |
|
1117 list->obj = sublist_ptr; |
|
1118 list->update = ui_listbox_list_update; |
|
1119 } |
|
1120 } |
|
1121 // fill items |
|
1122 ui_listbox_update(uilistbox, 0, cxListSize(uilistbox->sublists)); |
|
1123 |
|
1124 // register uilistbox for both widgets, so it doesn't matter which |
|
1125 // widget is used later |
|
1126 g_object_set_data(G_OBJECT(scroll_area), "ui_listbox", uilistbox); |
|
1127 g_object_set_data(G_OBJECT(listbox), "ui_listbox", uilistbox); |
|
1128 |
|
1129 // signals |
|
1130 g_signal_connect( |
|
1131 listbox, |
|
1132 "destroy", |
|
1133 G_CALLBACK(ui_destroy_sourcelist), |
|
1134 uilistbox); |
|
1135 |
|
1136 if(args.onactivate) { |
|
1137 g_signal_connect( |
|
1138 listbox, |
|
1139 "row-activated", |
|
1140 G_CALLBACK(ui_listbox_row_activate), |
|
1141 NULL); |
|
1142 } |
|
1143 |
|
1144 return scroll_area; |
|
1145 } |
|
1146 |
|
1147 void ui_listbox_update(UiListBox *listbox, int from, int to) { |
|
1148 CxIterator i = cxListIterator(listbox->sublists); |
|
1149 size_t pos = 0; |
|
1150 cx_foreach(UiListBoxSubList *, sublist, i) { |
|
1151 if(i.index < from) { |
|
1152 pos += sublist->numitems; |
|
1153 continue; |
|
1154 } |
|
1155 if(i.index > to) { |
|
1156 break; |
|
1157 } |
|
1158 |
|
1159 // reload sublist |
|
1160 ui_listbox_update_sublist(listbox, sublist, pos); |
|
1161 pos += sublist->numitems; |
|
1162 } |
|
1163 } |
|
1164 |
|
1165 static GtkWidget* create_listbox_row(UiListBox *listbox, UiListBoxSubList *sublist, UiSubListItem *item, int index) { |
|
1166 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10); |
|
1167 if(item->icon) { |
|
1168 GtkWidget *icon = ICON_IMAGE(item->icon); |
|
1169 BOX_ADD(hbox, icon); |
|
1170 } |
|
1171 GtkWidget *label = gtk_label_new(item->label); |
|
1172 gtk_widget_set_halign(label, GTK_ALIGN_START); |
|
1173 BOX_ADD_EXPAND(hbox, label); |
|
1174 // TODO: badge, button |
|
1175 GtkWidget *row = gtk_list_box_row_new(); |
|
1176 LISTBOX_ROW_SET_CHILD(row, hbox); |
|
1177 |
|
1178 // signals |
|
1179 UiEventDataExt *event = malloc(sizeof(UiEventDataExt)); |
|
1180 memset(event, 0, sizeof(UiEventDataExt)); |
|
1181 event->obj = listbox->obj; |
|
1182 event->customdata0 = sublist; |
|
1183 event->customdata1 = sublist->var; |
|
1184 event->customdata2 = item->eventdata; |
|
1185 event->callback = listbox->onactivate; |
|
1186 event->userdata = listbox->onactivatedata; |
|
1187 event->callback2 = listbox->onbuttonclick; |
|
1188 event->userdata2 = listbox->onbuttonclickdata; |
|
1189 event->value0 = index; |
|
1190 |
|
1191 g_signal_connect( |
|
1192 row, |
|
1193 "destroy", |
|
1194 G_CALLBACK(ui_destroy_userdata), |
|
1195 event); |
|
1196 |
|
1197 g_object_set_data(G_OBJECT(row), "ui-listbox-row-eventdata", event); |
|
1198 |
|
1199 return row; |
|
1200 } |
|
1201 |
|
1202 void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, size_t listbox_insert_index) { |
|
1203 // clear sublist |
|
1204 CxIterator r = cxListIterator(sublist->widgets); |
|
1205 cx_foreach(GtkWidget*, widget, r) { |
|
1206 LISTBOX_REMOVE(listbox->listbox, widget); |
|
1207 } |
|
1208 cxListClear(sublist->widgets); |
|
1209 |
|
1210 sublist->numitems = 0; |
|
1211 |
|
1212 // create items for each UiList element |
|
1213 UiList *list = sublist->var->value; |
|
1214 if(!list) { |
|
1215 return; |
|
1216 } |
|
1217 |
|
1218 size_t index = 0; |
|
1219 void *elm = list->first(list); |
|
1220 while(elm) { |
|
1221 UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL }; |
|
1222 listbox->getvalue(sublist->userdata, elm, index, &item); |
|
1223 |
|
1224 // create listbox item |
|
1225 GtkWidget *row = create_listbox_row(listbox, sublist, &item, (int)index); |
|
1226 if(index == 0) { |
|
1227 // first row in the sublist, set ui_listbox data to the row |
|
1228 // which is then used by the headerfunc |
|
1229 g_object_set_data(G_OBJECT(row), "ui_listbox", listbox); |
|
1230 g_object_set_data(G_OBJECT(row), "ui_listbox_sublist", sublist); |
|
1231 |
|
1232 if(listbox_insert_index == 0) { |
|
1233 // first row in the GtkListBox |
|
1234 listbox->first_row = GTK_LIST_BOX_ROW(row); |
|
1235 } |
|
1236 } |
|
1237 intptr_t rowindex = listbox_insert_index + index; |
|
1238 g_object_set_data(G_OBJECT(row), "ui_listbox_row_index", (gpointer)rowindex); |
|
1239 gtk_list_box_insert(listbox->listbox, row, listbox_insert_index + index); |
|
1240 cxListAdd(sublist->widgets, row); |
|
1241 |
|
1242 // cleanup |
|
1243 free(item.label); |
|
1244 free(item.icon); |
|
1245 free(item.button_label); |
|
1246 free(item.button_icon); |
|
1247 free(item.badge); |
|
1248 |
|
1249 // next row |
|
1250 elm = list->next(list); |
|
1251 index++; |
|
1252 } |
|
1253 |
|
1254 sublist->numitems = cxListSize(sublist->widgets); |
|
1255 } |
|
1256 |
|
1257 void ui_listbox_list_update(UiList *list, int i) { |
|
1258 UiListBoxSubList *sublist = list->obj; |
|
1259 } |
|
1260 |
|
1261 void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) { |
|
1262 UiEventDataExt *data = g_object_get_data(G_OBJECT(row), "ui-listbox-row-eventdata"); |
|
1263 if(!data) { |
|
1264 return; |
|
1265 } |
|
1266 UiListBoxSubList *sublist = data->customdata0; |
|
1267 |
|
1268 UiEvent event; |
|
1269 event.obj = data->obj; |
|
1270 event.window = event.obj->window; |
|
1271 event.document = event.obj->ctx->document; |
|
1272 event.eventdata = data->customdata2; |
|
1273 event.intval = data->value0; |
|
1274 |
|
1275 if(data->callback) { |
|
1276 data->callback(&event, data->userdata); |
|
1277 } |
|
1278 } |