diff --git a/exercise_common.c b/exercise_common.c index 5a2bfc5..f7315a6 100644 --- a/exercise_common.c +++ b/exercise_common.c @@ -78,6 +78,73 @@ GtkWidget *meaning_add(GtkWidget *body, const char *text) { return lbl; } +GtkWidget *mcq_option_toggle(const char *text, int index, + GtkToggleButton **group_first) { + GtkWidget *tb; + GtkWidget *lab; + char *labeled; + + labeled = g_strdup_printf("%c %s", 'A' + index, text); + tb = gtk_toggle_button_new(); + lab = gtk_label_new(labeled); + g_free(labeled); + + gtk_label_set_wrap(GTK_LABEL(lab), TRUE); + gtk_label_set_wrap_mode(GTK_LABEL(lab), PANGO_WRAP_WORD_CHAR); + gtk_label_set_xalign(GTK_LABEL(lab), 0.0); + gtk_widget_set_halign(lab, GTK_ALIGN_FILL); + gtk_widget_set_hexpand(lab, TRUE); + gtk_button_set_child(GTK_BUTTON(tb), lab); + + gtk_widget_add_css_class(tb, "pill"); + gtk_widget_add_css_class(tb, "quiz-pill"); + gtk_widget_set_hexpand(tb, TRUE); + gtk_widget_set_halign(tb, GTK_ALIGN_FILL); + + gtk_toggle_button_set_group(GTK_TOGGLE_BUTTON(tb), *group_first); + if (!*group_first) + *group_first = GTK_TOGGLE_BUTTON(tb); + + return tb; +} + +GtkWidget *mcq_append_question(GtkWidget *parent, int num, const ChoiceQ *q, + GtkToggleButton **out_toggles) { + GtkWidget *card; + GtkWidget *prompt; + GtkWidget *opts; + GtkToggleButton *first = NULL; + char *qtext; + int o; + + card = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10); + gtk_widget_add_css_class(card, "quiz-card"); + gtk_widget_set_hexpand(card, TRUE); + gtk_box_append(GTK_BOX(parent), card); + + qtext = g_strdup_printf("%d.) %s", num, q->prompt); + prompt = gtk_label_new(qtext); + g_free(qtext); + gtk_widget_set_halign(prompt, GTK_ALIGN_START); + gtk_label_set_wrap(GTK_LABEL(prompt), TRUE); + gtk_label_set_xalign(GTK_LABEL(prompt), 0.0); + gtk_widget_add_css_class(prompt, "quiz-q"); + gtk_box_append(GTK_BOX(card), prompt); + + opts = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8); + gtk_widget_add_css_class(opts, "quiz-options"); + gtk_box_append(GTK_BOX(card), opts); + + for (o = 0; o < q->n_options; o++) { + GtkWidget *tb = mcq_option_toggle(q->options[o], o, &first); + + gtk_box_append(GTK_BOX(opts), tb); + out_toggles[o] = GTK_TOGGLE_BUTTON(tb); + } + + return card; +} + /* A hidden green label that shows the model German sentence after Check. */ GtkWidget *model_answer_add(GtkWidget *body, const char *german) { GtkWidget *lbl = gtk_label_new(german); diff --git a/exercise_u1.c b/exercise_u1.c index 9744f7f..c4e757e 100644 --- a/exercise_u1.c +++ b/exercise_u1.c @@ -88,33 +88,11 @@ GtkWidget *build_choice(UnitCtx *unit, const char *title, ctx->trans = g_new0(GtkWidget *, n); for (int i = 0; i < n; i++) { - GtkWidget *prompt = gtk_label_new(qs[i].prompt); - GtkWidget *row; - GtkToggleButton *first = NULL; - - gtk_widget_set_halign(prompt, GTK_ALIGN_START); - gtk_widget_add_css_class(prompt, "ex-prompt"); - gtk_box_append(GTK_BOX(body), prompt); - - row = gtk_flow_box_new(); - gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(row), GTK_SELECTION_NONE); - gtk_flow_box_set_min_children_per_line(GTK_FLOW_BOX(row), n_opts); - gtk_flow_box_set_max_children_per_line(GTK_FLOW_BOX(row), n_opts); - gtk_widget_set_halign(row, GTK_ALIGN_START); - gtk_box_append(GTK_BOX(body), row); - - for (int o = 0; o < qs[i].n_options; o++) { - GtkWidget *tb = gtk_toggle_button_new_with_label(qs[i].options[o]); - gtk_widget_add_css_class(tb, "pill"); - gtk_toggle_button_set_group(GTK_TOGGLE_BUTTON(tb), first); - if (!first) - first = GTK_TOGGLE_BUTTON(tb); - gtk_flow_box_append(GTK_FLOW_BOX(row), tb); - ctx->toggles[i * n_opts + o] = GTK_TOGGLE_BUTTON(tb); - } + GtkWidget *card = mcq_append_question(body, i + 1, &qs[i], + &ctx->toggles[i * n_opts]); if (meanings[i]) - ctx->trans[i] = meaning_add(body, meanings[i]); + ctx->trans[i] = meaning_add(card, meanings[i]); } g_signal_connect(check, "clicked", G_CALLBACK(choice_check), ctx); diff --git a/hw.c b/hw.c index 37f8bac..7040d1a 100644 --- a/hw.c +++ b/hw.c @@ -653,22 +653,27 @@ static GtkWidget *build_hw_mcq_page(int lesson_id, const char *back_page, gtk_widget_set_margin_top(scroll, 12); gtk_box_append(GTK_BOX(page), scroll); - body = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10); + body = gtk_box_new(GTK_ORIENTATION_VERTICAL, 14); + gtk_widget_set_halign(body, GTK_ALIGN_FILL); + gtk_widget_set_hexpand(body, TRUE); + gtk_widget_set_margin_start(body, 4); + gtk_widget_set_margin_end(body, 4); gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), body); head = gtk_label_new(NULL); i18n_bind(head, heading_key, 0); gtk_widget_set_halign(head, GTK_ALIGN_START); gtk_label_set_wrap(GTK_LABEL(head), TRUE); - gtk_widget_set_margin_top(head, 6); - gtk_widget_add_css_class(head, "ex-sub"); + gtk_widget_set_margin_top(head, 4); + gtk_widget_add_css_class(head, "quiz-heading"); gtk_box_append(GTK_BOX(body), head); intro = gtk_label_new(NULL); i18n_bind(intro, "net_quiz_intro", 0); gtk_widget_set_halign(intro, GTK_ALIGN_START); gtk_label_set_wrap(GTK_LABEL(intro), TRUE); - gtk_widget_add_css_class(intro, "ex-prompt"); + gtk_widget_add_css_class(intro, "quiz-intro"); + gtk_widget_set_margin_bottom(intro, 4); gtk_box_append(GTK_BOX(body), intro); ctx->qs = qs; @@ -679,46 +684,20 @@ static GtkWidget *build_hw_mcq_page(int lesson_id, const char *back_page, ctx->hints = g_new0(GtkWidget *, n); for (int i = 0; i < n; i++) { - GtkWidget *prompt; - GtkWidget *row; - GtkToggleButton *first = NULL; - char *qtext; + GtkWidget *card = mcq_append_question(body, i + 1, &qs[i], + &ctx->toggles[i * n_opts]); - qtext = g_strdup_printf("%d.) %s", i + 1, qs[i].prompt); - prompt = gtk_label_new(qtext); - g_free(qtext); - gtk_widget_set_halign(prompt, GTK_ALIGN_START); - gtk_label_set_wrap(GTK_LABEL(prompt), TRUE); - gtk_widget_add_css_class(prompt, "ex-prompt"); - gtk_widget_set_margin_top(prompt, 8); - gtk_box_append(GTK_BOX(body), prompt); - - row = gtk_flow_box_new(); - gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(row), GTK_SELECTION_NONE); - gtk_flow_box_set_min_children_per_line(GTK_FLOW_BOX(row), n_opts); - gtk_flow_box_set_max_children_per_line(GTK_FLOW_BOX(row), n_opts); - gtk_widget_set_halign(row, GTK_ALIGN_START); - gtk_box_append(GTK_BOX(body), row); - - for (int o = 0; o < n_opts; o++) { - GtkWidget *tb = gtk_toggle_button_new_with_label(qs[i].options[o]); - gtk_widget_add_css_class(tb, "pill"); - gtk_toggle_button_set_group(GTK_TOGGLE_BUTTON(tb), first); - if (!first) - first = GTK_TOGGLE_BUTTON(tb); - gtk_flow_box_append(GTK_FLOW_BOX(row), tb); - ctx->toggles[i * n_opts + o] = GTK_TOGGLE_BUTTON(tb); - } - ctx->hints[i] = meaning_add(body, hints[i]); + ctx->hints[i] = meaning_add(card, hints[i]); } btns = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8); - gtk_widget_set_margin_top(btns, 12); + gtk_widget_set_margin_top(btns, 8); + gtk_widget_set_margin_bottom(btns, 8); gtk_box_append(GTK_BOX(body), btns); check = gtk_button_new(); i18n_bind(check, "check", 1); - gtk_widget_add_css_class(check, "pill"); + gtk_widget_add_css_class(check, "btn-primary"); gtk_widget_set_halign(check, GTK_ALIGN_START); gtk_box_append(GTK_BOX(btns), check); g_signal_connect(check, "clicked", G_CALLBACK(hw_mcq_check), ctx); diff --git a/maturita.h b/maturita.h index 5f8ff5f..eb2b12e 100644 --- a/maturita.h +++ b/maturita.h @@ -882,6 +882,10 @@ GtkWidget *ex_page_shell(const char *back_target, const char *title, GtkWidget **body_out, GtkWidget **feedback_out, GtkWidget **btn_out); GtkWidget *meaning_add(GtkWidget *body, const char *text); +GtkWidget *mcq_option_toggle(const char *text, int index, + GtkToggleButton **group_first); +GtkWidget *mcq_append_question(GtkWidget *parent, int num, const ChoiceQ *q, + GtkToggleButton **out_toggles); GtkWidget *model_answer_add(GtkWidget *body, const char *german); void meaning_reveal_all(GtkWidget **labels, int n); ComboListCtx *combo_list_ctx_new(UnitCtx *unit, int ex_num, diff --git a/net.c b/net.c index e93a1b5..f81a3e9 100644 --- a/net.c +++ b/net.c @@ -2872,22 +2872,27 @@ GtkWidget *build_net_mcq_page(int lesson_id, const char *back_page, gtk_widget_set_margin_top(scroll, 12); gtk_box_append(GTK_BOX(page), scroll); - body = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10); + body = gtk_box_new(GTK_ORIENTATION_VERTICAL, 14); + gtk_widget_set_halign(body, GTK_ALIGN_FILL); + gtk_widget_set_hexpand(body, TRUE); + gtk_widget_set_margin_start(body, 4); + gtk_widget_set_margin_end(body, 4); gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), body); head = gtk_label_new(NULL); i18n_bind(head, heading_key, 0); gtk_widget_set_halign(head, GTK_ALIGN_START); gtk_label_set_wrap(GTK_LABEL(head), TRUE); - gtk_widget_set_margin_top(head, 6); - gtk_widget_add_css_class(head, "ex-sub"); + gtk_widget_set_margin_top(head, 4); + gtk_widget_add_css_class(head, "quiz-heading"); gtk_box_append(GTK_BOX(body), head); intro = gtk_label_new(NULL); i18n_bind(intro, "net_quiz_intro", 0); gtk_widget_set_halign(intro, GTK_ALIGN_START); gtk_label_set_wrap(GTK_LABEL(intro), TRUE); - gtk_widget_add_css_class(intro, "ex-prompt"); + gtk_widget_add_css_class(intro, "quiz-intro"); + gtk_widget_set_margin_bottom(intro, 4); gtk_box_append(GTK_BOX(body), intro); ctx->qs = qs; @@ -2898,48 +2903,20 @@ GtkWidget *build_net_mcq_page(int lesson_id, const char *back_page, ctx->hints = g_new0(GtkWidget *, n); for (int i = 0; i < n; i++) { - GtkWidget *prompt; - GtkWidget *row; - GtkToggleButton *first = NULL; - char *qtext; + GtkWidget *card = mcq_append_question(body, i + 1, &qs[i], + &ctx->toggles[i * n_opts]); - qtext = g_strdup_printf("%d.) %s", i + 1, qs[i].prompt); - prompt = gtk_label_new(qtext); - g_free(qtext); - gtk_widget_set_halign(prompt, GTK_ALIGN_START); - gtk_label_set_wrap(GTK_LABEL(prompt), TRUE); - gtk_widget_add_css_class(prompt, "ex-prompt"); - gtk_widget_set_margin_top(prompt, 8); - gtk_box_append(GTK_BOX(body), prompt); - - row = gtk_flow_box_new(); - gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(row), GTK_SELECTION_NONE); - gtk_flow_box_set_min_children_per_line(GTK_FLOW_BOX(row), n_opts); - gtk_flow_box_set_max_children_per_line(GTK_FLOW_BOX(row), n_opts); - gtk_widget_set_halign(row, GTK_ALIGN_START); - gtk_box_append(GTK_BOX(body), row); - - for (int o = 0; o < n_opts; o++) { - GtkWidget *tb = gtk_toggle_button_new_with_label(qs[i].options[o]); - - gtk_widget_add_css_class(tb, "pill"); - gtk_toggle_button_set_group(GTK_TOGGLE_BUTTON(tb), first); - if (!first) - first = GTK_TOGGLE_BUTTON(tb); - gtk_flow_box_append(GTK_FLOW_BOX(row), tb); - ctx->toggles[i * n_opts + o] = GTK_TOGGLE_BUTTON(tb); - } - - ctx->hints[i] = meaning_add(body, hints[i]); + ctx->hints[i] = meaning_add(card, hints[i]); } btns = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8); - gtk_widget_set_margin_top(btns, 12); + gtk_widget_set_margin_top(btns, 8); + gtk_widget_set_margin_bottom(btns, 8); gtk_box_append(GTK_BOX(body), btns); check = gtk_button_new(); i18n_bind(check, "check", 1); - gtk_widget_add_css_class(check, "pill"); + gtk_widget_add_css_class(check, "btn-primary"); gtk_widget_set_halign(check, GTK_ALIGN_START); gtk_box_append(GTK_BOX(btns), check); g_signal_connect(check, "clicked", G_CALLBACK(net_mcq_check), ctx); diff --git a/style.css b/style.css index 6c3d001..c491f1a 100644 --- a/style.css +++ b/style.css @@ -498,7 +498,8 @@ entry.answer-wrong { border-radius: 999px; padding: 6px 18px; font-size: 15px; - transition: border-color 150ms ease, box-shadow 150ms ease; + transition: border-color 150ms ease, box-shadow 150ms ease, + background-color 150ms ease; } .pill:hover { background-image: none; @@ -517,19 +518,61 @@ entry.answer-wrong { border-color: @accent; font-weight: 700; } -.pill.ok { +.pill.ok, +.pill.ok:checked { + background-image: linear-gradient(135deg, @success 0%, @success2 100%); + color: @on_accent; border-color: @success; - box-shadow: 0 0 0 2px alpha(@success, 0.4); + box-shadow: 0 0 0 2px alpha(@success, 0.35); } -.pill.wrong { +.pill.wrong, +.pill.wrong:checked { + background-image: linear-gradient(135deg, @error 0%, #c45c5c 100%); + color: @on_accent; border-color: @error; - box-shadow: 0 0 0 2px alpha(@error, 0.4); + box-shadow: 0 0 0 2px alpha(@error, 0.35); } .pill.hm-key { padding: 4px 5px; min-width: 32px; font-size: 13px; } +.quiz-card { + background-color: alpha(@bg_surface0, 0.85); + border: 1px solid @bg_surface1; + border-radius: 18px; + padding: 16px 18px 14px 18px; + box-shadow: 0 6px 18px alpha(@bg_crust, 0.18); +} +.quiz-heading { + color: @fg_text; + font-size: 18px; + font-weight: 700; +} +.quiz-intro { + color: @fg_subtext; + font-size: 14px; + font-style: italic; +} +.quiz-q { + color: @fg_text; + font-size: 16px; + font-weight: 700; +} +.quiz-options { + margin-top: 2px; +} +.quiz-pill { + border-radius: 14px; + padding: 12px 16px; + min-height: 44px; +} +.quiz-pill label { + font-weight: 500; +} +.quiz-pill:checked label { + font-weight: 700; +} .chip { background-image: none; background-color: @bg_surface1;