ui/gtk/list.c

changeset 100
d2bd73d28ff1
parent 98
16e84fac48bd
equal deleted inserted replaced
99:b9767cb5b06b 100:d2bd73d28ff1
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 #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 }

mercurial