Last active
October 30, 2023 06:12
-
-
Save mrnugget/41f9c36e455e27958f853d2a4ba6551f to your computer and use it in GitHub Desktop.
GTK4: focus leaving last-focused-child in GtkNotebook and then back into it
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Compile with: | |
// gcc $(pkg-config --cflags gtk4) -o main ./main.c $(pkg-config --libs gtk4) && ./main | |
// | |
// Run with: | |
// ./main | |
// | |
// Reproduce issue: | |
// | |
// 1. Click on Page 2 | |
// 2. Select "view3" and type something. | |
// 3. Go back to Page1, focus should be inside "view1" | |
// 4. IMPORTANT: Now go back to Page 2 | |
// | |
// Expectation: "view3" is focused, because it was the last-focused element | |
// Actual: "view2" is focused! | |
// | |
// In console you can see that when switching to Page 2, focus leaves "view1", then goes into view3, but then out again! | |
// | |
// notebook: switch-page | |
// view1 focus: leave | |
// box1 focus: leave | |
// box2 focus: enter | |
// paned focus: enter | |
// view3 focus: enter | |
// view3 focus: leave <-- WHY? | |
// paned focus: leave <-- WHY? | |
// box2 focus: leave <-- WHY? | |
// box2 focus: enter <-- WHY? | |
// paned focus: enter <-- WHY? | |
// view2 focus: enter <-- WHY? | |
// | |
// WHY? | |
// | |
#include <gtk/gtk.h> | |
#include <stdio.h> | |
#include <string.h> | |
void focusEnter(GtkEventControllerFocus *ec, void *ud) { char *name = (char*)ud; printf("%s focus: enter\n", name); } | |
void focusLeave(GtkEventControllerFocus *ec, void *ud) { char *name = (char*)ud; printf("%s focus: leave\n", name); } | |
void addFocusController(GtkWidget *widget, char *name) { | |
GtkEventController* ec_focus = gtk_event_controller_focus_new(); | |
gtk_widget_add_controller(widget, ec_focus); | |
g_signal_connect_data(ec_focus, "enter", G_CALLBACK(&focusEnter), name, NULL, G_CONNECT_DEFAULT); | |
g_signal_connect_data(ec_focus, "leave", G_CALLBACK(&focusLeave), name, NULL, G_CONNECT_DEFAULT); | |
} | |
GtkWidget *createTextView(char *name) { | |
GtkWidget *widget = gtk_text_view_new(); | |
gtk_widget_set_hexpand(widget, TRUE); | |
gtk_widget_set_vexpand(widget, TRUE); | |
gtk_text_view_set_editable(GTK_TEXT_VIEW(widget), TRUE); | |
gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(widget), TRUE); | |
GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); | |
GtkTextIter endit; | |
gtk_text_buffer_get_end_iter(buf, &endit); | |
gtk_text_buffer_insert(buf, &endit, name, (gint)strlen(name)); | |
return widget; | |
} | |
void notebookEventHandler(GtkNotebook *n, GtkWidget *page, gsize idx, void *ud) { char *event = (char*)ud; printf("notebook: %s\n", event); } | |
static void activate(GtkApplication *app, gpointer user_data) { | |
// Setup window | |
GtkWidget *window = gtk_application_window_new(app); | |
gtk_window_set_title(GTK_WINDOW(window), "Window"); | |
gtk_window_set_default_size(GTK_WINDOW(window), 600, 600); | |
// Setup notebook | |
GtkWidget *notebook = gtk_notebook_new(); | |
gtk_widget_set_vexpand(notebook, TRUE); | |
gtk_widget_set_hexpand(notebook, TRUE); | |
gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_TOP); | |
gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE); | |
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), TRUE); | |
gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE); | |
char *switchPageEvent = "switch-page"; | |
g_signal_connect_data(notebook, switchPageEvent, G_CALLBACK(¬ebookEventHandler), switchPageEvent, NULL, G_CONNECT_DEFAULT); | |
// Add notebook to window | |
gtk_window_set_child(GTK_WINDOW(window), GTK_WIDGET(notebook)); | |
// Setup Page 1 | |
{ | |
GtkWidget *box1 = gtk_box_new(GTK_ORIENTATION_VERTICAL, FALSE); | |
gtk_widget_set_hexpand(box1, TRUE); | |
gtk_widget_set_vexpand(box1, TRUE); | |
addFocusController(box1, "box1"); | |
GtkWidget *view1 = createTextView("view1"); | |
addFocusController(view1, "view1"); | |
// View to box and box to notebook | |
gtk_box_append(GTK_BOX(box1), view1); | |
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), box1, NULL); | |
} | |
// Setup Page 2 | |
{ | |
GtkWidget *box2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, FALSE); | |
gtk_widget_set_hexpand(box2, TRUE); | |
gtk_widget_set_vexpand(box2, TRUE); | |
addFocusController(box2, "box2"); | |
GtkWidget *view2 = createTextView("view2"); | |
addFocusController(view2, "view2"); | |
GtkWidget *view3 = createTextView("view3"); | |
addFocusController(view3, "view3"); | |
GtkWidget *paned = gtk_paned_new(GTK_ORIENTATION_VERTICAL); | |
gtk_paned_set_start_child(GTK_PANED(paned), view2); | |
gtk_paned_set_end_child(GTK_PANED(paned), view3); | |
addFocusController(paned, "paned"); | |
// Paned to box and box to notebook | |
gtk_box_append(GTK_BOX(box2), paned); | |
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), box2, NULL); | |
} | |
gtk_widget_set_visible(window, TRUE); | |
} | |
int main(int argc, char **argv) { | |
GtkApplication *app; | |
int status; | |
app = gtk_application_new("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS); | |
g_signal_connect(app, "activate", G_CALLBACK(activate), NULL); | |
status = g_application_run(G_APPLICATION(app), argc, argv); | |
g_object_unref(app); | |
return status; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment