From 4566336b1bd5c63d694b87e378edd53c08d7c72e Mon Sep 17 00:00:00 2001 From: pepaskopec-blip Date: Thu, 17 Sep 2026 15:22:35 +0200 Subject: [PATCH] Make the learning path show one next step and a real finish flag. Every unlocked unit carried the "current" class, so three nodes lit up identically and none of them pointed anywhere. Mark only the first unlocked unit that is not finished; the rest stay open but quiet. The finish node filled its whole circle with chequers, which read as the placeholder pattern for a missing image rather than as a flag, most of all in light mode. Give it the plain face the other locked nodes have and put the chequers in a small badge where they show a padlock. Co-authored-by: Cursor --- progress.c | 40 +++++++++++++++++++++++++++++++--------- roadmap.c | 46 ++++++++++++++++++++++++---------------------- 2 files changed, 55 insertions(+), 31 deletions(-) diff --git a/progress.c b/progress.c index caac83e..79061f3 100644 --- a/progress.c +++ b/progress.c @@ -64,7 +64,24 @@ void all_rails_redraw(void) { } } +/* Index of the unit the student should work on next: the first unlocked one + * that is not finished yet. -1 once every open unit is complete. */ +static int current_unit_index(void) { + for (int uidx = 0; uidx < NUM_UNLOCKED; uidx++) { + UnitCtx *u = &units[uidx]; + + if (u->n_ex <= 0) + continue; + for (int i = 1; i <= u->n_ex; i++) + if (!u->done[i]) + return uidx; + } + return -1; +} + void refresh_completion_ui(void) { + int current = current_unit_index(); + for (int uidx = 0; uidx < NUM_UNLOCKED; uidx++) { UnitCtx *u = &units[uidx]; gboolean all = TRUE; @@ -107,19 +124,24 @@ void refresh_completion_ui(void) { } } - /* roadmap node marker */ + /* Roadmap node: finished, the one step to take next, or simply open. + * Highlighting every unlocked unit made three of them look equally + * "current" and told the student nothing. */ if (u->node) { - if (all && u->n_ex > 0) { - gtk_widget_remove_css_class(u->node, "current"); + gboolean done = all && u->n_ex > 0; + + if (done) gtk_widget_add_css_class(u->node, "done"); - if (u->node_done_icon) - gtk_widget_set_visible(u->node_done_icon, TRUE); - } else { + else gtk_widget_remove_css_class(u->node, "done"); + + if (!done && uidx == current) gtk_widget_add_css_class(u->node, "current"); - if (u->node_done_icon) - gtk_widget_set_visible(u->node_done_icon, FALSE); - } + else + gtk_widget_remove_css_class(u->node, "current"); + + if (u->node_done_icon) + gtk_widget_set_visible(u->node_done_icon, done); } } diff --git a/roadmap.c b/roadmap.c index bf902e5..6b1bf14 100644 --- a/roadmap.c +++ b/roadmap.c @@ -4,11 +4,15 @@ /* Roadmap page */ /* ------------------------------------------------------------------ */ -/* Draw the checkered "finish line" node: checkerboard behind the unit - * number and the same lock glyph the locked units use. */ +/* Draw the "finish line" node: a locked-looking circle with the unit number + * and a small chequered flag. The chequers used to fill the whole circle, + * which read as the placeholder pattern for a missing image rather than as a + * flag, so they are now a badge sitting on a plain node face. */ void draw_finish_cell(GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer data) { - const double s = 11.0; + const double cell = 6.0; /* chequer square */ + const int flag_cols = 3; + const int flag_rows = 2; const Rgb gray = color_from_hex(app_theme.overlay); const Rgb fin_dark = color_from_hex(app_theme.finish_dark); const Rgb fin_light = color_from_hex(app_theme.finish_light); @@ -17,15 +21,12 @@ void draw_finish_cell(GtkDrawingArea *area, cairo_t *cr, double cx = width / 2.0; double cy = height / 2.0; double r = MIN(width, height) / 2.0 - 2.0; - double start_x = cx - r; - double start_y = cy - r; - int cols = (int)ceil(2.0 * r / s); - int rows = (int)ceil(2.0 * r / s); char buf[8]; PangoLayout *layout; PangoFontDescription *fd; PangoRectangle ink; double num_cy; + double flag_x, flag_y; int ix, iy; (void)area; @@ -39,20 +40,10 @@ void draw_finish_cell(GtkDrawingArea *area, cairo_t *cr, cairo_arc(cr, cx, cy, r, 0.0, 2.0 * G_PI); cairo_clip(cr); - cairo_rectangle(cr, start_x, start_y, 2.0 * r, 2.0 * r); + cairo_rectangle(cr, cx - r, cy - r, 2.0 * r, 2.0 * r); cairo_set_source_rgb(cr, fin_dark.r, fin_dark.g, fin_dark.b); cairo_fill(cr); - for (iy = 0; iy < rows; iy++) { - for (ix = 0; ix < cols; ix++) { - if (((ix + iy) & 1) == 0) - continue; - cairo_rectangle(cr, start_x + ix * s, start_y + iy * s, s, s); - cairo_set_source_rgb(cr, fin_light.r, fin_light.g, fin_light.b); - cairo_fill(cr); - } - } - /* unit number, styled like the other locked nodes */ g_snprintf(buf, sizeof(buf), "%d", n); layout = pango_cairo_create_layout(cr); @@ -70,8 +61,19 @@ void draw_finish_cell(GtkDrawingArea *area, cairo_t *cr, num_cy - ink.height / 2.0 - ink.y); pango_cairo_show_layout(cr, layout); - /* lock glyph below the number, as on the other locked units */ - lock_paint(cr, cx - 8.0, num_cy + ink.height / 2.0 + 6.0, 16.0, &gray); + /* chequered flag below the number, where locked units show a padlock */ + flag_x = cx - (flag_cols * cell) / 2.0; + flag_y = num_cy + ink.height / 2.0 + 6.0; + for (iy = 0; iy < flag_rows; iy++) { + for (ix = 0; ix < flag_cols; ix++) { + const Rgb *c = ((ix + iy) & 1) ? &fin_light : &fin_stroke; + + cairo_rectangle(cr, flag_x + ix * cell, flag_y + iy * cell, + cell, cell); + cairo_set_source_rgb(cr, c->r, c->g, c->b); + cairo_fill(cr); + } + } g_object_unref(layout); pango_font_description_free(fd); @@ -293,10 +295,10 @@ void add_path_node(GtkFixed *fixed, int index) { GtkWidget *number; GtkWidget *icon; + /* Unlocked units start out plain; refresh_completion_ui() decides + * which single one is the current step. */ if (locked) gtk_widget_add_css_class(card, "locked"); - else - gtk_widget_add_css_class(card, "current"); vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2); gtk_widget_set_halign(vbox, GTK_ALIGN_CENTER);