12-15-2025, 12:22 PM
(This post was last modified: 12-15-2025, 12:29 PM by SpriggsySpriggs.)
What's the reason for using a dynamic library rather than compiling with a header into the code?
It works just fine by removing the EXPORT tags on the file, making it a .h header, adding the flags to the compiler in QB64, and then building.
It works just fine by removing the EXPORT tags on the file, making it a .h header, adding the flags to the compiler in QB64, and then building.
Code: (Select All)
'Declare Dynamic Library "listpick"
' Function PickFromListUTF8& (title As String, prompt As String, items As String, ByVal startIndex As Long)
' Function PickFromListUTF8_Text& (title As String, prompt As String, items As String, ByVal startIndex As Long, outText As String, ByVal outBytes As Long)
'End Declare
Declare CustomType Library "ListPick_GTK"
Function PickFromListUTF8& (title As String, prompt As String, items As String, ByVal startIndex As Long)
Function PickFromListUTF8_Text& (title As String, prompt As String, items As String, ByVal startIndex As Long, outText As String, ByVal outBytes As Long)
End Declare
' --- use ---
title$ = "Select item" + Chr$(0)
prompt$ = "Write for filter, Enter=OK, Esc=Cancel" + Chr$(0)
item:
Data "Alpha","Beta","Gamma","Delta","Etha","Theta","Omikron","Mi","Psi","Kocici","Kachni","Rozum","Zelenej","Linux","To je ale dilo"
Restore item
For f = 1 To 15
Read i$
items$ = items$ + i$ + Chr$(10)
Next f
items$ = items$ + Chr$(0)
out$ = Space$(2048) ' buffer, in which C write text
idx& = PickFromListUTF8_Text&(title$, prompt$, items$, 1, out$, Len(out$))
out$ = ZTrim$(out$)
Print "idx="; idx&, " text="; out$
Function ZTrim$ (s$)
Dim p As Long
p = InStr(s$, Chr$(0))
If p > 0 Then ZTrim$ = Left$(s$, p - 1) Else ZTrim$ = s$
End Function
Code: (Select All)
// listpick_gtk.c (GTK3, UTF-8)
// build (bash/zsh):
// gcc -O2 -fPIC -shared -o liblistpick.so listpick_gtk.c $(pkg-config --cflags --libs gtk+-3.0)
// build (fish):
// gcc -O2 -fPIC -shared -o liblistpick.so listpick_gtk.c (pkg-config --cflags --libs gtk+-3.0)
#include <gtk/gtk.h>
#include <string.h>
typedef struct {
GtkWidget *dialog;
GtkWidget *entry;
GtkWidget *list; // GtkListBox
GPtrArray *rows; // index(orig) -> GtkListBoxRow*
int bestOrig; // preferovaný origin index
} Picker;
static int gtk_ready = 0;
static gboolean row_is_effectively_visible(GtkWidget *w) {
// u GtkListBox filtrování typicky schovává řádky přes child_visible, ne přes visible
return gtk_widget_get_child_visible(w);
}
static gboolean contains_nocase_utf8(const char *hay, const char *needle) {
if (!needle || !*needle) return TRUE;
if (!hay) return FALSE;
char *h = g_utf8_casefold(hay, -1);
char *n = g_utf8_casefold(needle, -1);
gboolean ok = (g_strstr_len(h, -1, n) != NULL);
g_free(h);
g_free(n);
return ok;
}
static gboolean row_filter(GtkListBoxRow *row, gpointer user_data) {
Picker *p = (Picker *)user_data;
const char *needle = gtk_entry_get_text(GTK_ENTRY(p->entry));
if (!needle || !*needle) return TRUE;
GtkWidget *child = gtk_bin_get_child(GTK_BIN(row)); // GtkLabel
const char *text = gtk_label_get_text(GTK_LABEL(child));
return contains_nocase_utf8(text, needle);
}
static void ensure_selection(Picker *p) {
GtkListBox *box = GTK_LIST_BOX(p->list);
GtkListBoxRow *sel = gtk_list_box_get_selected_row(box);
if (sel && row_is_effectively_visible(GTK_WIDGET(sel))) return;
// 1) zkus bestOrig (naposledy vybraná položka), pokud je viditelná ve filtru
if (p->bestOrig >= 0 && p->bestOrig < (int)p->rows->len) {
GtkListBoxRow *want = (GtkListBoxRow *)g_ptr_array_index(p->rows, p->bestOrig);
if (want && row_is_effectively_visible(GTK_WIDGET(want))) {
gtk_list_box_select_row(box, want);
return;
}
}
// 2) první viditelný řádek
GList *children = gtk_container_get_children(GTK_CONTAINER(box));
for (GList *l = children; l; l = l->next) {
GtkWidget *w = GTK_WIDGET(l->data);
if (row_is_effectively_visible(w)) {
gtk_list_box_select_row(box, GTK_LIST_BOX_ROW(w));
break;
}
}
g_list_free(children);
}
static void on_entry_changed(GtkEditable *editable, gpointer user_data) {
(void)editable;
Picker *p = (Picker *)user_data;
gtk_list_box_invalidate_filter(GTK_LIST_BOX(p->list));
while (gtk_events_pending()) gtk_main_iteration(); // ať se child_visible přepočte hned
ensure_selection(p);
}
static void on_row_selected(GtkListBox *box, GtkListBoxRow *row, gpointer user_data) {
(void)box;
Picker *p = (Picker *)user_data;
if (!row) return;
int orig = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(row), "orig"));
p->bestOrig = orig;
}
static void on_row_activated(GtkListBox *box, GtkListBoxRow *row, gpointer user_data) {
(void)box; (void)row;
Picker *p = (Picker *)user_data;
gtk_dialog_response(GTK_DIALOG(p->dialog), GTK_RESPONSE_OK);
}
static gboolean on_key_press(GtkWidget *w, GdkEventKey *e, gpointer user_data) {
Picker *p = (Picker *)user_data;
if (e->keyval == GDK_KEY_Escape) {
gtk_dialog_response(GTK_DIALOG(p->dialog), GTK_RESPONSE_CANCEL);
return TRUE;
}
if (e->keyval == GDK_KEY_Return || e->keyval == GDK_KEY_KP_Enter) {
// před OK ještě pro jistotu dorovnej selection podle filtru
ensure_selection(p);
gtk_dialog_response(GTK_DIALOG(p->dialog), GTK_RESPONSE_OK);
return TRUE;
}
if (w == p->entry && (e->keyval == GDK_KEY_Down || e->keyval == GDK_KEY_Up)) {
gtk_widget_grab_focus(p->list);
return FALSE;
}
return FALSE;
}
static void copy_out(char *out, int outBytes, const char *src) {
if (!out || outBytes <= 0) return;
out[0] = 0;
if (!src) return;
g_strlcpy(out, src, (gsize)outBytes);
}
static int pick_run_utf8(const char *title, const char *prompt, const char *items, int startIndex,
char *out, int outBytes) {
if (!gtk_ready) {
int argc = 0;
char **argv = NULL;
gtk_ready = gtk_init_check(&argc, &argv) ? 1 : 0;
if (!gtk_ready) {
copy_out(out, outBytes, "");
return -1;
}
}
Picker p;
memset(&p, 0, sizeof(p));
p.bestOrig = (startIndex >= 0) ? startIndex : 0;
p.rows = g_ptr_array_new();
p.dialog = gtk_dialog_new_with_buttons(
title ? title : "Select",
NULL,
GTK_DIALOG_MODAL,
"_Cancel", GTK_RESPONSE_CANCEL,
"_OK", GTK_RESPONSE_OK,
NULL
);
gtk_window_set_default_size(GTK_WINDOW(p.dialog), 520, 380);
GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(p.dialog));
GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
gtk_widget_set_hexpand(vbox, TRUE);
gtk_widget_set_vexpand(vbox, TRUE);
gtk_box_pack_start(GTK_BOX(content), vbox, TRUE, TRUE, 0);
GtkWidget *lbl = gtk_label_new(prompt ? prompt : "");
gtk_label_set_xalign(GTK_LABEL(lbl), 0.0f);
gtk_label_set_line_wrap(GTK_LABEL(lbl), TRUE);
gtk_box_pack_start(GTK_BOX(vbox), lbl, FALSE, FALSE, 0);
p.entry = gtk_entry_new();
gtk_box_pack_start(GTK_BOX(vbox), p.entry, FALSE, FALSE, 0);
GtkWidget *sw = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_widget_set_hexpand(sw, TRUE);
gtk_widget_set_vexpand(sw, TRUE);
gtk_widget_set_halign(sw, GTK_ALIGN_FILL);
gtk_widget_set_valign(sw, GTK_ALIGN_FILL);
gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
p.list = gtk_list_box_new();
gtk_list_box_set_selection_mode(GTK_LIST_BOX(p.list), GTK_SELECTION_SINGLE);
gtk_widget_set_hexpand(p.list, TRUE);
gtk_widget_set_vexpand(p.list, TRUE);
gtk_widget_set_halign(p.list, GTK_ALIGN_FILL);
gtk_widget_set_valign(p.list, GTK_ALIGN_FILL);
gtk_container_add(GTK_CONTAINER(sw), p.list);
// naplň řádky (items jsou UTF-8, oddělené '\n')
const char *s = items ? items : "";
int orig = 0;
while (*s) {
const char *e = strchr(s, '\n');
size_t len = e ? (size_t)(e - s) : strlen(s);
while (len && s[len - 1] == '\r') len--;
char *line = g_strndup(s, len);
GtkWidget *row = gtk_list_box_row_new();
GtkWidget *t = gtk_label_new(line);
gtk_label_set_xalign(GTK_LABEL(t), 0.0f);
gtk_label_set_ellipsize(GTK_LABEL(t), PANGO_ELLIPSIZE_END);
gtk_container_add(GTK_CONTAINER(row), t);
g_object_set_data(G_OBJECT(row), "orig", GINT_TO_POINTER(orig));
gtk_list_box_insert(GTK_LIST_BOX(p.list), row, -1);
g_ptr_array_add(p.rows, row);
g_free(line);
orig++;
if (!e) break;
s = e + 1;
}
// filtering
gtk_list_box_set_filter_func(GTK_LIST_BOX(p.list), row_filter, &p, NULL);
// signály
g_signal_connect(p.entry, "changed", G_CALLBACK(on_entry_changed), &p);
g_signal_connect(p.list, "row-selected", G_CALLBACK(on_row_selected), &p);
g_signal_connect(p.list, "row-activated", G_CALLBACK(on_row_activated), &p);
g_signal_connect(p.entry, "key-press-event", G_CALLBACK(on_key_press), &p);
g_signal_connect(p.list, "key-press-event", G_CALLBACK(on_key_press), &p);
gtk_widget_show_all(p.list);
gtk_list_box_invalidate_filter(GTK_LIST_BOX(p.list));
gtk_widget_show_all(p.dialog);
// start selection
if (startIndex >= 0 && startIndex < (int)p.rows->len) {
GtkListBoxRow *r = (GtkListBoxRow *)g_ptr_array_index(p.rows, startIndex);
gtk_list_box_select_row(GTK_LIST_BOX(p.list), r);
} else {
ensure_selection(&p);
}
gtk_widget_grab_focus(p.entry);
int resp = gtk_dialog_run(GTK_DIALOG(p.dialog));
// při OK ještě jednou dorovnej selection podle filtru (klik na OK myší by jinak mohl vrátit starou selection)
if (resp == GTK_RESPONSE_OK) {
gtk_list_box_invalidate_filter(GTK_LIST_BOX(p.list));
while (gtk_events_pending()) gtk_main_iteration();
ensure_selection(&p);
}
int result = -1;
if (resp == GTK_RESPONSE_OK) {
GtkListBoxRow *sel = gtk_list_box_get_selected_row(GTK_LIST_BOX(p.list));
if (sel && row_is_effectively_visible(GTK_WIDGET(sel))) {
result = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(sel), "orig"));
GtkWidget *child = gtk_bin_get_child(GTK_BIN(sel));
copy_out(out, outBytes, gtk_label_get_text(GTK_LABEL(child)));
} else {
copy_out(out, outBytes, "");
result = -1;
}
} else {
copy_out(out, outBytes, "");
result = -1;
}
gtk_widget_destroy(p.dialog);
while (gtk_events_pending()) gtk_main_iteration();
g_ptr_array_free(p.rows, TRUE);
return result;
}
// -------- exporty pro QB64PE (UTF-8) --------
int PickFromListUTF8(const char *titleUtf8, const char *promptUtf8,
const char *itemsUtf8, int startIndex) {
return pick_run_utf8(titleUtf8, promptUtf8, itemsUtf8, startIndex, NULL, 0);
}
int PickFromListUTF8_Text(const char *titleUtf8, const char *promptUtf8,
const char *itemsUtf8, int startIndex,
char *outUtf8, int outBytes) {
return pick_run_utf8(titleUtf8, promptUtf8, itemsUtf8, startIndex, outUtf8, outBytes);
}
The noticing will continue

