From 704e47ef15f850bfb8e46d52b8e69430e457ea33 Mon Sep 17 00:00:00 2001 From: Martin Dvorak Date: Thu, 3 Sep 2026 16:19:22 +0200 Subject: [PATCH] added a roadmap for the future units --- sprechen.c | 242 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 221 insertions(+), 21 deletions(-) diff --git a/sprechen.c b/sprechen.c index 9f56570..a634d0e 100644 --- a/sprechen.c +++ b/sprechen.c @@ -1,40 +1,39 @@ #include -static void activate(GtkApplication *app, gpointer user_data) { - GtkWindow *window; - GtkWidget *headerbar; - GtkWidget *title_label; +#define NUM_UNITS 12 +#define FIRST_LOCKED 1 + +static GtkStack *main_stack; +static GtkWidget *back_button; + +static void on_continue_clicked(GtkButton *button, gpointer user_data) { + gtk_stack_set_visible_child_name(main_stack, "roadmap"); + gtk_widget_set_visible(back_button, TRUE); +} + +static void on_back_clicked(GtkButton *button, gpointer user_data) { + gtk_stack_set_visible_child_name(main_stack, "welcome"); + gtk_widget_set_visible(back_button, FALSE); +} + +static GtkWidget *build_welcome_page(void) { GtkWidget *box; GtkWidget *heading; GtkWidget *card; GtkWidget *button; - GtkCssProvider *provider; - - // Create a new application window - window = GTK_WINDOW(gtk_application_window_new(app)); - gtk_window_set_title(window, "Sprechen.c"); - gtk_window_set_default_size(window, 720, 520); - - // Create a styled headerbar (titlebar) - headerbar = gtk_header_bar_new(); - gtk_widget_add_css_class(headerbar, "titlebar"); - title_label = gtk_label_new("Sprechen.c"); - gtk_widget_add_css_class(title_label, "app-title"); - gtk_header_bar_set_title_widget(GTK_HEADER_BAR(headerbar), title_label); - gtk_window_set_titlebar(window, headerbar); // Create a vertical box to hold contents box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18); gtk_widget_set_halign(box, GTK_ALIGN_CENTER); gtk_widget_set_valign(box, GTK_ALIGN_CENTER); - gtk_widget_set_margin_top(GTK_WIDGET(box), 40); - gtk_widget_set_margin_bottom(GTK_WIDGET(box), 40); - gtk_window_set_child(window, box); + gtk_widget_set_margin_top(box, 40); + gtk_widget_set_margin_bottom(box, 40); // Heading heading = gtk_label_new(NULL); gtk_label_set_markup(GTK_LABEL(heading), "Vítejte ve Sprechen.C!"); + gtk_widget_set_halign(heading, GTK_ALIGN_CENTER); gtk_widget_add_css_class(heading, "heading"); gtk_box_append(GTK_BOX(box), heading); @@ -56,6 +55,149 @@ static void activate(GtkApplication *app, gpointer user_data) { gtk_widget_add_css_class(button, "btn-primary"); gtk_box_append(GTK_BOX(box), button); + g_signal_connect(button, "clicked", G_CALLBACK(on_continue_clicked), NULL); + + return box; +} + +static GtkWidget *build_roadmap_page(void) { + GtkWidget *page; + GtkWidget *heading; + GtkWidget *sub; + GtkWidget *scroll; + GtkWidget *flow; + int i; + + page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4); + gtk_widget_set_hexpand(page, TRUE); + gtk_widget_set_vexpand(page, TRUE); + gtk_widget_set_margin_start(page, 32); + gtk_widget_set_margin_end(page, 32); + gtk_widget_set_margin_top(page, 28); + gtk_widget_set_margin_bottom(page, 24); + + // Page heading + heading = gtk_label_new("Učební plán"); + gtk_widget_set_halign(heading, GTK_ALIGN_CENTER); + gtk_widget_add_css_class(heading, "roadmap-title"); + gtk_box_append(GTK_BOX(page), heading); + + sub = gtk_label_new("Vyberte jednotku a začněte procvičovat."); + gtk_widget_set_halign(sub, GTK_ALIGN_CENTER); + gtk_widget_add_css_class(sub, "roadmap-sub"); + gtk_box_append(GTK_BOX(page), sub); + + // Scrollable grid of unit cards + scroll = gtk_scrolled_window_new(); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_widget_set_vexpand(scroll, TRUE); + gtk_widget_set_margin_top(scroll, 18); + gtk_box_append(GTK_BOX(page), scroll); + + flow = gtk_flow_box_new(); + gtk_flow_box_set_homogeneous(GTK_FLOW_BOX(flow), TRUE); + gtk_flow_box_set_column_spacing(GTK_FLOW_BOX(flow), 16); + gtk_flow_box_set_row_spacing(GTK_FLOW_BOX(flow), 16); + gtk_flow_box_set_max_children_per_line(GTK_FLOW_BOX(flow), 3); + gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(flow), GTK_SELECTION_NONE); + gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), flow); + + for (i = 0; i < NUM_UNITS; i++) { + int num = i + 1; + gboolean locked = i >= FIRST_LOCKED; + GtkWidget *card; + GtkWidget *vbox; + GtkWidget *badge; + GtkWidget *row; + GtkWidget *title; + char *text; + + card = gtk_button_new(); + gtk_widget_add_css_class(card, "unit-card"); + gtk_widget_set_sensitive(card, !locked); + gtk_widget_set_size_request(card, 190, 170); + if (locked) + gtk_widget_add_css_class(card, "locked"); + + vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10); + gtk_widget_set_halign(vbox, GTK_ALIGN_CENTER); + gtk_widget_set_valign(vbox, GTK_ALIGN_CENTER); + gtk_button_set_child(GTK_BUTTON(card), vbox); + + // Number badge + text = g_strdup_printf("%d", num); + badge = gtk_label_new(text); + g_free(text); + gtk_widget_add_css_class(badge, "unit-badge"); + gtk_box_append(GTK_BOX(vbox), badge); + + // Title row (with lock icon when locked) + row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); + gtk_widget_set_halign(row, GTK_ALIGN_CENTER); + gtk_box_append(GTK_BOX(vbox), row); + + if (locked) { + GtkWidget *lock = gtk_image_new_from_icon_name("system-lock-screen-symbolic"); + gtk_image_set_pixel_size(GTK_IMAGE(lock), 14); + gtk_widget_add_css_class(lock, "lock-icon"); + gtk_box_append(GTK_BOX(row), lock); + } + + text = g_strdup_printf("Jednotka %d", num); + title = gtk_label_new(text); + g_free(text); + gtk_widget_add_css_class(title, "unit-title"); + gtk_box_append(GTK_BOX(row), title); + + gtk_flow_box_insert(GTK_FLOW_BOX(flow), card, -1); + } + + return page; +} + +static void activate(GtkApplication *app, gpointer user_data) { + GtkWindow *window; + GtkWidget *headerbar; + GtkWidget *title_label; + GtkWidget *welcome_page; + GtkWidget *roadmap_page; + GtkCssProvider *provider; + + // Create a new application window + window = GTK_WINDOW(gtk_application_window_new(app)); + gtk_window_set_title(window, "Sprechen.c"); + gtk_window_set_default_size(window, 760, 560); + + // Create a styled headerbar (titlebar) + headerbar = gtk_header_bar_new(); + gtk_widget_add_css_class(headerbar, "titlebar"); + title_label = gtk_label_new("Sprechen.c"); + gtk_widget_add_css_class(title_label, "app-title"); + gtk_header_bar_set_title_widget(GTK_HEADER_BAR(headerbar), title_label); + + // Back button (shown only on the roadmap page) + back_button = gtk_button_new_from_icon_name("go-previous-symbolic"); + gtk_widget_add_css_class(back_button, "back-btn"); + gtk_widget_set_visible(back_button, FALSE); + gtk_header_bar_pack_start(GTK_HEADER_BAR(headerbar), back_button); + g_signal_connect(back_button, "clicked", G_CALLBACK(on_back_clicked), NULL); + + gtk_window_set_titlebar(window, headerbar); + + // Stack navigation between welcome and roadmap pages + main_stack = GTK_STACK(gtk_stack_new()); + gtk_stack_set_transition_type(GTK_STACK(main_stack), + GTK_STACK_TRANSITION_TYPE_CROSSFADE); + gtk_window_set_child(window, GTK_WIDGET(main_stack)); + + welcome_page = build_welcome_page(); + roadmap_page = build_roadmap_page(); + + gtk_stack_add_named(main_stack, welcome_page, "welcome"); + gtk_stack_add_named(main_stack, roadmap_page, "roadmap"); + gtk_stack_set_visible_child_name(main_stack, "welcome"); + // Load Catppuccin Mocha CSS provider = gtk_css_provider_new(); gtk_css_provider_load_from_string(provider, @@ -104,6 +246,64 @@ static void activate(GtkApplication *app, gpointer user_data) { ".btn-primary:active {" " box-shadow: 0 3px 10px rgba(203, 166, 247, 0.35);" "}" + ".roadmap-title {" + " color: #cdd6f4;" + " font-size: 28px;" + " font-weight: 800;" + "}" + ".roadmap-sub {" + " color: #a6adc8;" + " font-size: 14px;" + "}" + ".unit-card {" + " background-color: rgba(30, 30, 46, 0.65);" + " border: 1px solid #45475a;" + " border-radius: 20px;" + " padding: 24px 18px;" + " box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);" + " transition: transform 150ms ease, box-shadow 150ms ease, border-color 150ms ease;" + "}" + ".unit-card:hover {" + " transform: translateY(-3px);" + " border-color: #cba6f7;" + " box-shadow: 0 10px 26px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(203, 166, 247, 0.25);" + "}" + ".unit-card:active {" + " transform: translateY(0px);" + "}" + ".unit-card.locked {" + " opacity: 0.55;" + " background-color: rgba(30, 30, 46, 0.5);" + " box-shadow: none;" + "}" + ".unit-badge {" + " min-width: 48px;" + " min-height: 48px;" + " border-radius: 999px;" + " padding: 6px 14px;" + " color: #11111b;" + " background-image: linear-gradient(135deg, #cba6f7 0%, #b4befe 100%);" + " font-size: 22px;" + " font-weight: 800;" + " box-shadow: 0 4px 12px rgba(203, 166, 247, 0.35);" + "}" + ".unit-card.locked .unit-badge {" + " background-image: none;" + " background-color: #45475a;" + " color: #a6adc8;" + " box-shadow: none;" + "}" + ".unit-title {" + " color: #cdd6f4;" + " font-size: 15px;" + " font-weight: 600;" + "}" + ".unit-card.locked .unit-title {" + " color: #a6adc8;" + "}" + ".lock-icon {" + " color: #6c7086;" + "}" ); // Apply CSS to the window