Skip to content

Instantly share code, notes, and snippets.

@mrnugget
Last active October 30, 2023 06:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrnugget/41f9c36e455e27958f853d2a4ba6551f to your computer and use it in GitHub Desktop.
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
// 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(&notebookEventHandler), 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