prepared the czech subject

This commit is contained in:
Martin Dvořák 2026-09-14 12:44:21 +02:00
commit cf84bffb36
8 changed files with 227 additions and 5 deletions

152
czech.c Normal file
View file

@ -0,0 +1,152 @@
#include "maturita.h"
/* ---- Czech language and literature (Český jazyk a literatura) ----- */
#define CZ_NODES 3
#define CZ_BUBBLE SUB_BUBBLE
#define CZ_LABEL_W 200.0
#define CZ_CW 600
#define CZ_CH 470
static const char *cz_keys[CZ_NODES] = {
"Literatura", "Mluvnice", "Maturitní četba",
};
static const char *cz_initials[CZ_NODES] = {
"L", "M", "Č",
};
static GtkWidget *cz_rail;
static double cz_cx[CZ_NODES];
static double cz_cy[CZ_NODES];
static void cz_geometry(void) {
cz_cx[0] = 300.0; cz_cy[0] = 120.0; /* Literatura (top) */
cz_cx[1] = 140.0; cz_cy[1] = 350.0; /* Mluvnice (bottom-left) */
cz_cx[2] = 460.0; cz_cy[2] = 350.0; /* Maturitní četba (bottom-right) */
}
/* Three bubbles joined into a triangle by thick rails. */
static void draw_cz_rail(GtkDrawingArea *area, cairo_t *cr,
int width, int height, gpointer data) {
const Rgb rail = color_from_hex(app_theme.rail);
const Rgb slate = color_from_hex(app_theme.surface1);
(void)area;
(void)width;
(void)height;
(void)data;
cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
cairo_paint(cr);
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);
cairo_new_path(cr);
cairo_move_to(cr, cz_cx[0], cz_cy[0]);
cairo_line_to(cr, cz_cx[1], cz_cy[1]);
cairo_line_to(cr, cz_cx[2], cz_cy[2]);
cairo_close_path(cr);
cairo_set_line_width(cr, 14);
cairo_set_source_rgb(cr, rail.r, rail.g, rail.b);
cairo_stroke_preserve(cr);
cairo_set_line_width(cr, 9);
cairo_set_source_rgb(cr, slate.r, slate.g, slate.b);
cairo_stroke(cr);
}
void czech_rail_theme_reset(void) {
if (cz_rail)
gtk_widget_queue_draw(cz_rail);
}
static GtkWidget *cz_make_node(int index) {
GtkWidget *btn;
GtkWidget *label;
btn = gtk_button_new();
gtk_widget_add_css_class(btn, "unit-node");
gtk_widget_add_css_class(btn, "current");
gtk_widget_set_can_focus(btn, FALSE);
gtk_widget_set_size_request(btn, (int)CZ_BUBBLE, (int)CZ_BUBBLE);
label = gtk_label_new(cz_initials[index]);
gtk_widget_add_css_class(label, "unit-number");
gtk_button_set_child(GTK_BUTTON(btn), label);
return btn;
}
GtkWidget *build_czechmap_page(void) {
GtkWidget *page;
GtkWidget *scroll;
GtkWidget *wrap;
GtkWidget *fixed;
GtkWidget *rail;
cz_geometry();
page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
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, 24);
gtk_widget_set_margin_bottom(page, 24);
gtk_box_append(GTK_BOX(page),
top_bar("subjects", "Český jazyk a literatura",
"czech_sub"));
scroll = gtk_scrolled_window_new();
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_widget_set_vexpand(scroll, TRUE);
gtk_widget_set_margin_top(scroll, 14);
gtk_box_append(GTK_BOX(page), scroll);
wrap = gtk_center_box_new();
gtk_widget_set_vexpand(wrap, TRUE);
gtk_widget_set_hexpand(wrap, TRUE);
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), wrap);
fixed = gtk_fixed_new();
gtk_widget_set_halign(fixed, GTK_ALIGN_CENTER);
gtk_widget_set_valign(fixed, GTK_ALIGN_CENTER);
gtk_widget_set_size_request(fixed, CZ_CW, CZ_CH);
gtk_center_box_set_center_widget(GTK_CENTER_BOX(wrap), fixed);
rail = gtk_drawing_area_new();
gtk_widget_set_size_request(rail, CZ_CW, CZ_CH);
gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(rail), draw_cz_rail,
NULL, NULL);
gtk_fixed_put(GTK_FIXED(fixed), rail, 0, 0);
cz_rail = rail;
for (int i = 0; i < CZ_NODES; i++) {
GtkWidget *btn = cz_make_node(i);
GtkWidget *name = gtk_label_new(NULL);
i18n_bind(name, cz_keys[i], 0);
gtk_widget_set_size_request(name, (int)CZ_LABEL_W, -1);
gtk_widget_set_halign(name, GTK_ALIGN_CENTER);
gtk_label_set_justify(GTK_LABEL(name), GTK_JUSTIFY_CENTER);
gtk_label_set_wrap(GTK_LABEL(name), TRUE);
gtk_widget_add_css_class(name, "unit-name");
gtk_widget_add_css_class(name, "cz-label");
gtk_fixed_put(GTK_FIXED(fixed), btn, 0, 0);
gtk_fixed_put(GTK_FIXED(fixed), name, 0, 0);
gtk_fixed_move(GTK_FIXED(fixed), btn,
(int)(cz_cx[i] - CZ_BUBBLE / 2.0),
(int)(cz_cy[i] - CZ_BUBBLE / 2.0));
gtk_fixed_move(GTK_FIXED(fixed), name,
(int)(cz_cx[i] - CZ_LABEL_W / 2.0),
(int)(cz_cy[i] + CZ_BUBBLE / 2.0 + 10.0));
}
return page;
}

5
i18n.c
View file

@ -369,6 +369,11 @@ const TrEntry tr_ui[] = {
"Quiz: data representation"}, "Quiz: data representation"},
{"hw_quiz3_head", "Kvíz k zobrazování dat v počítači", {"hw_quiz3_head", "Kvíz k zobrazování dat v počítači",
"Quiz on data representation in computers"}, "Quiz on data representation in computers"},
{"czech_sub", "Vyberte oblast a začněte procvičovat.",
"Pick an area and start practicing."},
{"Literatura", "Literatura", "Literature"},
{"Mluvnice", "Mluvnice", "Grammar"},
{"Maturitní četba", "Maturitní četba", "Maturita reading list"},
{NULL, NULL, NULL} {NULL, NULL, NULL}
}; };

1
main.c
View file

@ -231,6 +231,7 @@ void activate(GtkApplication *app, gpointer user_data) {
gtk_stack_add_named(main_stack, build_stats_page(), "stats"); gtk_stack_add_named(main_stack, build_stats_page(), "stats");
gtk_stack_add_named(main_stack, build_netmap_page(), "netmap"); gtk_stack_add_named(main_stack, build_netmap_page(), "netmap");
gtk_stack_add_named(main_stack, build_hwmap_page(), "hwmap"); gtk_stack_add_named(main_stack, build_hwmap_page(), "hwmap");
gtk_stack_add_named(main_stack, build_czechmap_page(), "czechmap");
gtk_stack_add_named(main_stack, build_hw_unit1_page(), "hwunit1"); gtk_stack_add_named(main_stack, build_hw_unit1_page(), "hwunit1");
gtk_stack_add_named(main_stack, build_hw_unit1_exercise_page(), "hwex1"); gtk_stack_add_named(main_stack, build_hw_unit1_exercise_page(), "hwex1");
gtk_stack_add_named(main_stack, build_hw_unit2_page(), "hwunit2"); gtk_stack_add_named(main_stack, build_hw_unit2_page(), "hwunit2");

View file

@ -13,6 +13,7 @@
#define NUM_SUBJECTS 13 #define NUM_SUBJECTS 13
#define NET_SUBJ 1 /* index of "Správa počítačových sítí" */ #define NET_SUBJ 1 /* index of "Správa počítačových sítí" */
#define HW_SUBJ 2 /* index of "Technické vybavení" */ #define HW_SUBJ 2 /* index of "Technické vybavení" */
#define CZ_SUBJ 3 /* index of "Český jazyk a literatura" */
#define CSS_FILE "style.css" #define CSS_FILE "style.css"
#define PROGRESS_DIR "progress" #define PROGRESS_DIR "progress"
#define PROGRESS_U1 "progress/unit1.conf" #define PROGRESS_U1 "progress/unit1.conf"
@ -786,8 +787,10 @@ void mark_hw_done(int lesson_id);
void refresh_hw_completion_ui(void); void refresh_hw_completion_ui(void);
void progress_for_hw(ProgressSum *out); void progress_for_hw(ProgressSum *out);
void hw_rail_theme_reset(void); void hw_rail_theme_reset(void);
void czech_rail_theme_reset(void);
void hw_lessons_apply_lang(void); void hw_lessons_apply_lang(void);
GtkWidget *build_hwmap_page(void); GtkWidget *build_hwmap_page(void);
GtkWidget *build_czechmap_page(void);
GtkWidget *build_hw_unit1_page(void); GtkWidget *build_hw_unit1_page(void);
GtkWidget *build_hw_unit1_exercise_page(void); GtkWidget *build_hw_unit1_exercise_page(void);
GtkWidget *build_hw_unit2_page(void); GtkWidget *build_hw_unit2_page(void);
@ -796,6 +799,8 @@ GtkWidget *build_hw_unit3_page(void);
GtkWidget *build_hw_unit3_exercise_page(void); GtkWidget *build_hw_unit3_exercise_page(void);
void draw_chip_icon(GtkDrawingArea *area, cairo_t *cr, void draw_chip_icon(GtkDrawingArea *area, cairo_t *cr,
int width, int height, gpointer data); int width, int height, gpointer data);
void draw_czech_flag(GtkDrawingArea *area, cairo_t *cr,
int width, int height, gpointer data);
void net_lesson_notes_ensure(NetLesson *L); void net_lesson_notes_ensure(NetLesson *L);
void net_rescale_lesson_notes(NetLesson *L, int body, int head, int kick); void net_rescale_lesson_notes(NetLesson *L, int body, int head, int kick);
GtkWidget *make_back_button(const char *target); GtkWidget *make_back_button(const char *target);

View file

@ -86,7 +86,7 @@ void refresh_stats_ui(void) {
GtkWidget *bar = stats_ui.subj[s].bar; GtkWidget *bar = stats_ui.subj[s].bar;
ProgressSum sp = {0}; ProgressSum sp = {0};
gboolean has_content = (s == NET_SUBJ) || (s == HW_SUBJ) gboolean has_content = (s == NET_SUBJ) || (s == HW_SUBJ)
|| sub_unit_count[s] > 0; || (s == CZ_SUBJ) || sub_unit_count[s] > 0;
if (!count || !bar) if (!count || !bar)
continue; continue;
@ -359,7 +359,7 @@ GtkWidget *build_stats_page(void) {
GtkWidget *count; GtkWidget *count;
GtkWidget *bar; GtkWidget *bar;
gboolean has_units = (s == NET_SUBJ) || (s == HW_SUBJ) gboolean has_units = (s == NET_SUBJ) || (s == HW_SUBJ)
|| sub_unit_count[s] > 0; || (s == CZ_SUBJ) || sub_unit_count[s] > 0;
gboolean has_deutsch_units = sub_unit_count[s] > 0; gboolean has_deutsch_units = sub_unit_count[s] > 0;
subject = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); subject = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);

View file

@ -366,6 +366,11 @@ windowcontrols button:not(:hover) image {
font-size: 12px; font-size: 12px;
font-weight: 600; font-weight: 600;
} }
.cz-label {
background-color: @bg_base;
padding: 1px 6px;
border-radius: 8px;
}
.ex-prompt { .ex-prompt {
color: @fg_text; color: @fg_text;
font-size: 16px; font-size: 16px;

View file

@ -5,11 +5,13 @@
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */
/* Index 0 (Deutsch), NET_SUBJ (networks) and HW_SUBJ (hardware) are unlocked. */ /* Index 0 (Deutsch), NET_SUBJ (networks), HW_SUBJ (hardware) and CZ_SUBJ
* (Czech) are unlocked. */
const char *sub_keys[NUM_SUBJECTS] = { const char *sub_keys[NUM_SUBJECTS] = {
"Deutsch", "Deutsch",
"Správa počítačových sítí", "Správa počítačových sítí",
"Technické vybavení", "Technické vybavení",
"Český jazyk a literatura",
"Občanská nauka", "Občanská nauka",
"English", "English",
"Matematika", "Matematika",
@ -17,7 +19,6 @@ const char *sub_keys[NUM_SUBJECTS] = {
"Základy Přírodopisných věd", "Základy Přírodopisných věd",
"Technická grafika", "Technická grafika",
"Prezentační grafika", "Prezentační grafika",
"Český jazyk a literatura",
"Programování", "Programování",
"Programové vybavení", "Programové vybavení",
}; };
@ -245,6 +246,47 @@ void draw_german_flag(GtkDrawingArea *area, cairo_t *cr,
cairo_restore(cr); cairo_restore(cr);
} }
/* Czech flag (white / red with a blue triangle) drawn in a clipped circle. */
void draw_czech_flag(GtkDrawingArea *area, cairo_t *cr,
int width, int height, gpointer data) {
double cx = width / 2.0;
double cy = height / 2.0;
double r = MIN(width, height) / 2.0 - 2.0;
(void)area;
(void)data;
cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
cairo_paint(cr);
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
cairo_save(cr);
cairo_new_path(cr);
cairo_arc(cr, cx, cy, r, 0.0, 2.0 * G_PI);
cairo_clip(cr);
cairo_new_path(cr);
cairo_rectangle(cr, 0.0, 0.0, (double)width, (double)height);
cairo_set_source_rgb(cr, 0.98, 0.98, 0.98);
cairo_fill(cr);
cairo_new_path(cr);
cairo_rectangle(cr, 0.0, (double)height / 2.0,
(double)width, (double)height / 2.0);
cairo_set_source_rgb(cr, 0.83, 0.16, 0.18);
cairo_fill(cr);
cairo_new_path(cr);
cairo_move_to(cr, 0.0, 0.0);
cairo_line_to(cr, (double)width * 0.55, (double)height / 2.0);
cairo_line_to(cr, 0.0, (double)height);
cairo_close_path(cr);
cairo_set_source_rgb(cr, 0.07, 0.24, 0.55);
cairo_fill(cr);
cairo_restore(cr);
}
GtkWidget *build_subjects_page(void) { GtkWidget *build_subjects_page(void) {
GtkWidget *page; GtkWidget *page;
GtkWidget *scroll; GtkWidget *scroll;
@ -337,6 +379,17 @@ GtkWidget *build_subjects_page(void) {
g_object_set_data_full(G_OBJECT(card), "target", g_object_set_data_full(G_OBJECT(card), "target",
g_strdup("hwmap"), g_free); g_strdup("hwmap"), g_free);
g_signal_connect(card, "clicked", G_CALLBACK(on_nav_clicked), NULL); g_signal_connect(card, "clicked", G_CALLBACK(on_nav_clicked), NULL);
} else if (i == CZ_SUBJ) {
GtkWidget *flag = gtk_drawing_area_new();
gtk_widget_add_css_class(card, "current");
gtk_widget_set_size_request(flag, (int)SUB_BUBBLE,
(int)SUB_BUBBLE);
gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(flag),
draw_czech_flag, NULL, NULL);
gtk_button_set_child(GTK_BUTTON(card), flag);
g_object_set_data_full(G_OBJECT(card), "target",
g_strdup("czechmap"), g_free);
g_signal_connect(card, "clicked", G_CALLBACK(on_nav_clicked), NULL);
} else { } else {
GtkWidget *lock; GtkWidget *lock;
gtk_widget_add_css_class(card, "locked"); gtk_widget_add_css_class(card, "locked");
@ -356,7 +409,7 @@ GtkWidget *build_subjects_page(void) {
gtk_label_set_justify(GTK_LABEL(lbl), GTK_JUSTIFY_CENTER); gtk_label_set_justify(GTK_LABEL(lbl), GTK_JUSTIFY_CENTER);
gtk_label_set_wrap(GTK_LABEL(lbl), TRUE); gtk_label_set_wrap(GTK_LABEL(lbl), TRUE);
gtk_widget_add_css_class(lbl, "unit-name"); gtk_widget_add_css_class(lbl, "unit-name");
if (i != 0 && i != NET_SUBJ && i != HW_SUBJ) if (i != 0 && i != NET_SUBJ && i != HW_SUBJ && i != CZ_SUBJ)
gtk_widget_add_css_class(lbl, "unit-name-locked"); gtk_widget_add_css_class(lbl, "unit-name-locked");
sub_labels[i] = lbl; sub_labels[i] = lbl;
gtk_fixed_put(GTK_FIXED(fixed), lbl, 0, 0); gtk_fixed_put(GTK_FIXED(fixed), lbl, 0, 0);

View file

@ -414,4 +414,5 @@ void apply_theme(void) {
} }
net_rail_theme_reset(); net_rail_theme_reset();
hw_rail_theme_reset(); hw_rail_theme_reset();
czech_rail_theme_reset();
} }