Add short transitions for navigation, answers and progress on every platform.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
pepaskopec-blip 2026-10-08 21:13:32 +02:00
commit 59d40c886a
32 changed files with 359 additions and 121 deletions

View file

@ -1282,7 +1282,7 @@ static void on_stub_quiz_clicked(GtkButton *button, gpointer user_data) {
(void)user_data;
cetba_prepare_stub_quiz();
if (main_stack)
gtk_stack_set_visible_child_name(main_stack, "cetbastubquiz");
show_page("cetbastubquiz", 1);
}
static void on_stub_plot_clicked(GtkButton *button, gpointer user_data) {
@ -1290,7 +1290,7 @@ static void on_stub_plot_clicked(GtkButton *button, gpointer user_data) {
(void)user_data;
cetba_prepare_stub_plot();
if (main_stack)
gtk_stack_set_visible_child_name(main_stack, "cetbastubdej");
show_page("cetbastubdej", 1);
}
static GtkWidget *book_rail;
@ -1467,7 +1467,7 @@ static void on_book_node_clicked(GtkButton *button, gpointer user_data) {
rebuild_stub_about(idx);
}
if (main_stack && target)
gtk_stack_set_visible_child_name(main_stack, target);
show_page(target, 1);
}
static GtkWidget *book_make_node(int i) {
@ -1810,7 +1810,7 @@ void cetba_open_stub(int idx) {
gtk_label_set_text(GTK_LABEL(stub_title_lbl), book_defs[idx].title);
rebuild_stub_about(idx);
if (main_stack)
gtk_stack_set_visible_child_name(main_stack, "cetbastub");
show_page("cetbastub", 1);
}
void cetba_register_search(void) {

View file

@ -416,7 +416,7 @@ static void en_open_unit(GtkButton *button, gpointer data) {
if (L->next_btn)
gtk_button_set_label(GTK_BUTTON(L->next_btn), tr("net_slide_next"));
}
gtk_stack_set_visible_child_name(main_stack, L->unit_page);
show_page(L->unit_page, 1);
}
static void en_add_node(EngMap *m, GtkFixed *fixed, int index) {
@ -699,7 +699,7 @@ static void en_slide_next(GtkButton *button, gpointer data) {
L->idx++;
skill_slide_apply(L);
} else {
gtk_stack_set_visible_child_name(main_stack, L->ex_page);
show_page(L->ex_page, 1);
}
}

View file

@ -220,7 +220,7 @@ static void fyz_open_unit(GtkButton *button, gpointer data) {
if (L->next_btn)
gtk_button_set_label(GTK_BUTTON(L->next_btn), tr("net_slide_next"));
}
gtk_stack_set_visible_child_name(main_stack, L->unit_page);
show_page(L->unit_page, 1);
}
static GtkWidget *fyz_lesson_button(FyzTrack *T, int index) {
@ -460,7 +460,7 @@ static void fyz_slide_next(GtkButton *button, gpointer data) {
L->idx++;
fyz_slide_apply(L);
} else {
gtk_stack_set_visible_child_name(main_stack, L->ex_page);
show_page(L->ex_page, 1);
}
}

View file

@ -1199,6 +1199,7 @@ void draw_bulb_icon(GtkDrawingArea *area, cairo_t *cr,
void net_lesson_notes_ensure(NetLesson *L);
void net_rescale_lesson_notes(NetLesson *L, int body, int head, int kick);
GtkWidget *make_back_button(const char *target);
void show_page(const char *name, int motion);
void on_nav_clicked(GtkButton *button, gpointer user_data);
int unit_done_count(const UnitCtx *u);
void progress_for_units(int first, int n, ProgressSum *out);

View file

@ -661,7 +661,7 @@ void hw_slide_next(GtkButton *button, gpointer data) {
L->idx++;
hw_slide_apply(L);
} else {
gtk_stack_set_visible_child_name(main_stack, L->ex_page);
show_page(L->ex_page, 1);
}
}

View file

@ -377,7 +377,7 @@ static void lit_open_unit(GtkButton *button, gpointer data) {
if (L->next_btn)
gtk_button_set_label(GTK_BUTTON(L->next_btn), tr("net_slide_next"));
}
gtk_stack_set_visible_child_name(main_stack, L->unit_page);
show_page(L->unit_page, 1);
}
static void lit_add_node(LitTrack *T, GtkFixed *fixed, int index) {
@ -634,7 +634,7 @@ static void lit_slide_next(GtkButton *button, gpointer data) {
L->idx++;
lit_slide_apply(L);
} else {
gtk_stack_set_visible_child_name(main_stack, L->ex_page);
show_page(L->ex_page, 1);
}
}

View file

@ -736,10 +736,13 @@ void activate(GtkApplication *app, gpointer user_data) {
const char *size = g_getenv("MATURITA_SIZE");
int w, h;
gtk_stack_set_transition_type(main_stack, GTK_STACK_TRANSITION_TYPE_NONE);
if (start && *start)
gtk_stack_set_visible_child_name(main_stack, start);
else
gtk_stack_set_visible_child_name(main_stack, "welcome");
gtk_stack_set_transition_type(main_stack, GTK_STACK_TRANSITION_TYPE_CROSSFADE);
gtk_stack_set_transition_duration(main_stack, 180);
if (size && sscanf(size, "%dx%d", &w, &h) == 2)
gtk_window_set_default_size(window, w, h);
}

View file

@ -226,7 +226,7 @@ static void math_open_unit(GtkButton *button, gpointer data) {
if (L->next_btn)
gtk_button_set_label(GTK_BUTTON(L->next_btn), tr("net_slide_next"));
}
gtk_stack_set_visible_child_name(main_stack, L->unit_page);
show_page(L->unit_page, 1);
}
static GtkWidget *math_lesson_button(MathTrack *T, int index) {
@ -469,7 +469,7 @@ static void math_slide_next(GtkButton *button, gpointer data) {
L->idx++;
math_slide_apply(L);
} else {
gtk_stack_set_visible_child_name(main_stack, L->ex_page);
show_page(L->ex_page, 1);
}
}

View file

@ -4,17 +4,40 @@
/* Navigation */
/* ------------------------------------------------------------------ */
/* motion: -1 slides back, 0 crossfades, 1 slides forward. */
void show_page(const char *name, int motion) {
GtkStackTransitionType type = GTK_STACK_TRANSITION_TYPE_CROSSFADE;
guint ms = 180;
if (!main_stack || !name || !name[0])
return;
if (!gtk_stack_get_child_by_name(main_stack, name))
return;
if (motion > 0) {
type = GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT;
ms = 200;
} else if (motion < 0) {
type = GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT;
ms = 200;
}
gtk_stack_set_transition_type(main_stack, type);
gtk_stack_set_transition_duration(main_stack, ms);
gtk_stack_set_visible_child_name(main_stack, name);
}
void on_nav_clicked(GtkButton *button, gpointer user_data) {
const char *target = g_object_get_data(G_OBJECT(button), "target");
int back = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button), "nav-back"));
(void)user_data;
if (target)
gtk_stack_set_visible_child_name(main_stack, target);
show_page(target, back ? -1 : 1);
}
void on_continue_clicked(GtkButton *button, gpointer user_data) {
(void)button;
(void)user_data;
gtk_stack_set_visible_child_name(main_stack, "subjects");
show_page("subjects", 1);
}
GtkWidget *make_back_button(const char *target) {
@ -31,6 +54,7 @@ GtkWidget *make_back_button(const char *target) {
g_signal_connect_swapped(back, "state-flags-changed",
G_CALLBACK(gtk_widget_queue_draw), icon);
g_object_set_data_full(G_OBJECT(back), "target", g_strdup(target), g_free);
g_object_set_data(G_OBJECT(back), "nav-back", GINT_TO_POINTER(1));
g_signal_connect(back, "clicked", G_CALLBACK(on_nav_clicked), NULL);
return back;
}

View file

@ -195,7 +195,7 @@ void net_open_unit(GtkButton *button, gpointer data) {
return;
L->idx = 0;
net_slide_apply(L);
gtk_stack_set_visible_child_name(main_stack, L->unit_page);
show_page(L->unit_page, 1);
}
void net_slide_prev(GtkButton *button, gpointer data) {
@ -220,7 +220,7 @@ void net_slide_next(GtkButton *button, gpointer data) {
L->idx++;
net_slide_apply(L);
} else {
gtk_stack_set_visible_child_name(main_stack, L->ex_page);
show_page(L->ex_page, 1);
}
}

View file

@ -266,7 +266,7 @@ static void on_open_unit(GtkButton *button, gpointer data) {
if (L->next_btn)
gtk_button_set_label(GTK_BUTTON(L->next_btn), tr("net_slide_next"));
}
gtk_stack_set_visible_child_name(main_stack, L->unit_page);
show_page(L->unit_page, 1);
}
static void on_add_node(GtkFixed *fixed, int index) {
@ -660,7 +660,7 @@ static void on_slide_next(GtkButton *button, gpointer data) {
L->idx++;
on_slide_apply(L);
} else {
gtk_stack_set_visible_child_name(main_stack, L->ex_page);
show_page(L->ex_page, 1);
}
}

View file

@ -278,7 +278,7 @@ static void on2_open_unit(GtkButton *button, gpointer data) {
if (L->next_btn)
gtk_button_set_label(GTK_BUTTON(L->next_btn), tr("net_slide_next"));
}
gtk_stack_set_visible_child_name(main_stack, L->unit_page);
show_page(L->unit_page, 1);
}
static void on2_add_node(GtkFixed *fixed, int index) {
@ -566,7 +566,7 @@ static void on2_slide_next(GtkButton *button, gpointer data) {
L->idx++;
on2_slide_apply(L);
} else {
gtk_stack_set_visible_child_name(main_stack, L->ex_page);
show_page(L->ex_page, 1);
}
}

View file

@ -277,7 +277,7 @@ static void on3_open_unit(GtkButton *button, gpointer data) {
if (L->next_btn)
gtk_button_set_label(GTK_BUTTON(L->next_btn), tr("net_slide_next"));
}
gtk_stack_set_visible_child_name(main_stack, L->unit_page);
show_page(L->unit_page, 1);
}
static void on3_add_node(GtkFixed *fixed, int index) {
@ -565,7 +565,7 @@ static void on3_slide_next(GtkButton *button, gpointer data) {
L->idx++;
on3_slide_apply(L);
} else {
gtk_stack_set_visible_child_name(main_stack, L->ex_page);
show_page(L->ex_page, 1);
}
}

View file

@ -278,7 +278,7 @@ static void on4_open_unit(GtkButton *button, gpointer data) {
if (L->next_btn)
gtk_button_set_label(GTK_BUTTON(L->next_btn), tr("net_slide_next"));
}
gtk_stack_set_visible_child_name(main_stack, L->unit_page);
show_page(L->unit_page, 1);
}
static void on4_add_node(GtkFixed *fixed, int index) {
@ -566,7 +566,7 @@ static void on4_slide_next(GtkButton *button, gpointer data) {
L->idx++;
on4_slide_apply(L);
} else {
gtk_stack_set_visible_child_name(main_stack, L->ex_page);
show_page(L->ex_page, 1);
}
}

View file

@ -191,7 +191,7 @@ static void sci_open_unit(GtkButton *button, gpointer data) {
if (L->next_btn)
gtk_button_set_label(GTK_BUTTON(L->next_btn), tr("net_slide_next"));
}
gtk_stack_set_visible_child_name(main_stack, L->unit_page);
show_page(L->unit_page, 1);
}
static GtkWidget *sci_lesson_button(SciTrack *T, int index) {
@ -460,7 +460,7 @@ static void sci_slide_next(GtkButton *button, gpointer data) {
L->idx++;
sci_slide_apply(L);
} else {
gtk_stack_set_visible_child_name(main_stack, L->ex_page);
show_page(L->ex_page, 1);
}
}

View file

@ -205,7 +205,7 @@ static void qy_slide_next(GtkButton *button, gpointer data) {
L->idx++;
skill_slide_apply(L);
} else if (L->ex_page) {
gtk_stack_set_visible_child_name(main_stack, L->ex_page);
show_page(L->ex_page, 1);
}
}

View file

@ -16,6 +16,8 @@ typedef struct {
} SearchItem;
static GtkWidget *search_layer;
static GtkWidget *search_reveal;
static gboolean search_want_open;
static GtkWidget *search_entry;
static GtkWidget *search_list;
static GtkWidget *search_empty;
@ -472,7 +474,7 @@ static void search_go(const char *target) {
if (!gtk_stack_get_child_by_name(main_stack, target))
return;
search_close();
gtk_stack_set_visible_child_name(main_stack, target);
show_page(target, 1);
}
static void on_row_activated(GtkListBox *box, GtkListBoxRow *row,
@ -641,19 +643,40 @@ static void on_scrim_clicked(GtkGestureClick *g, gint n_press, gdouble x,
search_close();
}
static void on_search_revealed(GObject *obj, GParamSpec *pspec, gpointer data) {
GtkRevealer *reveal = GTK_REVEALER(obj);
(void)pspec;
(void)data;
if (!gtk_revealer_get_reveal_child(reveal) &&
!gtk_revealer_get_child_revealed(reveal))
gtk_widget_set_visible(GTK_WIDGET(reveal), FALSE);
}
gboolean search_is_open(void) {
return search_layer && gtk_widget_get_visible(search_layer);
return search_want_open;
}
void search_close(void) {
if (search_layer)
gtk_widget_set_visible(search_layer, FALSE);
search_want_open = FALSE;
if (search_reveal)
gtk_revealer_set_reveal_child(GTK_REVEALER(search_reveal), FALSE);
}
static gboolean reveal_search(gpointer data) {
(void)data;
if (search_want_open && search_reveal)
gtk_revealer_set_reveal_child(GTK_REVEALER(search_reveal), TRUE);
return G_SOURCE_REMOVE;
}
void search_open(void) {
if (!search_layer)
if (!search_reveal)
return;
gtk_widget_set_visible(search_layer, TRUE);
search_want_open = TRUE;
gtk_widget_set_visible(search_reveal, TRUE);
/* Wait a frame so the overlay is mapped and the fade actually runs. */
g_idle_add(reveal_search, NULL);
if (search_entry) {
gtk_editable_set_text(GTK_EDITABLE(search_entry), "");
gtk_widget_grab_focus(search_entry);
@ -711,7 +734,6 @@ void search_attach(GtkOverlay *overlay) {
gtk_widget_add_css_class(search_layer, "search-layer");
gtk_widget_set_hexpand(search_layer, TRUE);
gtk_widget_set_vexpand(search_layer, TRUE);
gtk_widget_set_visible(search_layer, FALSE);
scrim = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_add_css_class(scrim, "search-scrim");
@ -763,5 +785,16 @@ void search_attach(GtkOverlay *overlay) {
g_signal_connect(search_list, "row-activated",
G_CALLBACK(on_row_activated), NULL);
gtk_overlay_add_overlay(overlay, search_layer);
search_reveal = gtk_revealer_new();
gtk_revealer_set_transition_type(GTK_REVEALER(search_reveal),
GTK_REVEALER_TRANSITION_TYPE_CROSSFADE);
gtk_revealer_set_transition_duration(GTK_REVEALER(search_reveal), 160);
gtk_revealer_set_reveal_child(GTK_REVEALER(search_reveal), FALSE);
gtk_widget_set_hexpand(search_reveal, TRUE);
gtk_widget_set_vexpand(search_reveal, TRUE);
gtk_widget_set_visible(search_reveal, FALSE);
gtk_revealer_set_child(GTK_REVEALER(search_reveal), search_layer);
g_signal_connect(search_reveal, "notify::child-revealed",
G_CALLBACK(on_search_revealed), NULL);
gtk_overlay_add_overlay(overlay, search_reveal);
}

View file

@ -212,7 +212,7 @@ void on_stats_back_clicked(GtkButton *button, gpointer user_data) {
(void)user_data;
if (!target || !target[0])
target = "subjects";
gtk_stack_set_visible_child_name(main_stack, target);
show_page(target, -1);
}
void on_stats_clicked(GtkButton *button, gpointer user_data) {
@ -227,7 +227,7 @@ void on_stats_clicked(GtkButton *button, gpointer user_data) {
stats_return_page = g_strdup(cur);
}
refresh_stats_ui();
gtk_stack_set_visible_child_name(main_stack, "stats");
show_page("stats", 0);
}
GtkWidget *stats_metric_card(const char *label_key, GtkWidget **value_out) {

View file

@ -413,7 +413,7 @@ static void update_refresh_ui(void) {
gtk_widget_set_visible(up.progress, up.state == UPDATE_DOWNLOADING);
if (up.banner) {
gtk_widget_set_visible(up.banner, banner);
gtk_revealer_set_reveal_child(GTK_REVEALER(up.banner), banner);
if (banner) {
gtk_label_set_text(GTK_LABEL(up.banner_label), status);
gtk_button_set_label(GTK_BUTTON(up.banner_action), tr(action));
@ -1019,7 +1019,7 @@ static void on_update_dismiss(GtkButton *btn, gpointer data) {
(void)btn;
(void)data;
if (up.banner)
gtk_widget_set_visible(up.banner, FALSE);
gtk_revealer_set_reveal_child(GTK_REVEALER(up.banner), FALSE);
}
/* Strip shown above the page stack once an update is waiting. */
@ -1030,7 +1030,6 @@ GtkWidget *build_update_banner(void) {
GtkWidget *later = gtk_button_new();
gtk_widget_add_css_class(bar, "update-banner");
gtk_widget_set_visible(bar, FALSE);
gtk_widget_set_hexpand(label, TRUE);
gtk_label_set_xalign(GTK_LABEL(label), 0.0);
@ -1048,10 +1047,19 @@ GtkWidget *build_update_banner(void) {
g_signal_connect(later, "clicked", G_CALLBACK(on_update_dismiss), NULL);
gtk_box_append(GTK_BOX(bar), later);
up.banner = bar;
up.banner_label = label;
up.banner_action = action;
return bar;
{
GtkWidget *reveal = gtk_revealer_new();
gtk_revealer_set_transition_type(GTK_REVEALER(reveal),
GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN);
gtk_revealer_set_transition_duration(GTK_REVEALER(reveal), 180);
gtk_revealer_set_reveal_child(GTK_REVEALER(reveal), FALSE);
gtk_revealer_set_child(GTK_REVEALER(reveal), bar);
up.banner = reveal;
return reveal;
}
}
/* "Updates" section of the settings popover. */