mirror of
https://github.com/pepaskopec-blip/Graduately.git
synced 2026-10-27 15:14:35 +00:00
Refactor networks quizzes, progress tracking, and page wiring.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
1e84a1f663
commit
02c994fb5e
7 changed files with 406 additions and 945 deletions
37
README.md
37
README.md
|
|
@ -72,17 +72,17 @@ GTK 4 interface.
|
||||||
8. Přepojování – slides + quiz
|
8. Přepojování – slides + quiz
|
||||||
9. Vrstevnaté modely a protokoly – slides + quiz
|
9. Vrstevnaté modely a protokoly – slides + quiz
|
||||||
10. Model ISO/OSI a TCP/IP – slides + quiz
|
10. Model ISO/OSI a TCP/IP – slides + quiz
|
||||||
Progress for networks is **not** saved yet (unlike Deutsch); the Statistics
|
Progress for networks is saved to `progress/net.conf` (one flag per unit);
|
||||||
page still treats networks as without tracked units
|
completed network units show a checkmark on the path and count in Statistics
|
||||||
- **Progress tracking** – completed exercises are marked green; progress is
|
- **Progress tracking** – completed exercises are marked green; progress is
|
||||||
saved per unit to `progress/unit1.conf`, `progress/unit2.conf` and
|
saved per unit to `progress/unit1.conf`, `progress/unit2.conf` and
|
||||||
`progress/unit3.conf` and restored on startup
|
`progress/unit3.conf` and restored on startup
|
||||||
- **Statistics** – chart icon in the header opens a per-subject progress
|
- **Statistics** – chart icon in the header opens a per-subject progress
|
||||||
overview: summary totals (exercises done, percent, finished units) at the
|
overview: summary totals (exercises done, percent, finished units) at the
|
||||||
top, then every subject with its own progress bar (subjects without tracked
|
top, then every subject with its own progress bar (subjects without tracked
|
||||||
content yet — including networks for now — are shown as locked), and a
|
content yet are shown as locked; **networks** use lesson completion from
|
||||||
per-unit breakdown with progress bars for Deutsch; numbers update as soon
|
`progress/net.conf`), and a per-unit breakdown with progress bars for
|
||||||
as you finish an exercise
|
Deutsch; numbers update as soon as you finish an exercise
|
||||||
- **Settings** – gear icon in the top-right header opens a panel where you can
|
- **Settings** – gear icon in the top-right header opens a panel where you can
|
||||||
choose:
|
choose:
|
||||||
- **mode**: dark or light, as a segmented control
|
- **mode**: dark or light, as a segmented control
|
||||||
|
|
@ -295,15 +295,15 @@ When launched, the application shows the welcome screen. From there:
|
||||||
screen.
|
screen.
|
||||||
7. Open **Statistiky / Statistics** (chart icon, top-right) to see progress
|
7. Open **Statistiky / Statistics** (chart icon, top-right) to see progress
|
||||||
grouped per subject (overall totals plus, for Deutsch, a per-unit
|
grouped per subject (overall totals plus, for Deutsch, a per-unit
|
||||||
breakdown). Network progress is not tracked in Statistics yet.
|
breakdown). Network lesson completion is included in the subject totals.
|
||||||
8. Open **Nastavení / Settings** (gear icon, top-right) to switch dark/light
|
8. Open **Nastavení / Settings** (gear icon, top-right) to switch dark/light
|
||||||
mode, pick a color theme, or change the interface language.
|
mode, pick a color theme, or change the interface language.
|
||||||
|
|
||||||
German units 1–3 and network units 1–10 are currently implemented. Locked
|
German units 1–3 and network units 1–10 are currently implemented. Locked
|
||||||
nodes do nothing – they are placeholders until lessons are added. Completed
|
nodes do nothing – they are placeholders until lessons are added. Completed
|
||||||
Deutsch exercises stay green and are saved per unit to `progress/unit1.conf` /
|
Deutsch exercises stay green and are saved per unit to `progress/unit1.conf` /
|
||||||
`progress/unit2.conf` / `progress/unit3.conf` (created next to the app on first
|
`progress/unit2.conf` / `progress/unit3.conf`. Completed network units are
|
||||||
finish).
|
saved to `progress/net.conf`.
|
||||||
|
|
||||||
#### Keyboard shortcuts
|
#### Keyboard shortcuts
|
||||||
|
|
||||||
|
|
@ -347,6 +347,7 @@ progress/ created at runtime
|
||||||
unit1.conf unit 1 exercise completion state
|
unit1.conf unit 1 exercise completion state
|
||||||
unit2.conf unit 2 exercise completion state
|
unit2.conf unit 2 exercise completion state
|
||||||
unit3.conf unit 3 exercise completion state
|
unit3.conf unit 3 exercise completion state
|
||||||
|
net.conf computer-networks lesson completion
|
||||||
settings.conf theme, dark/light mode and language preference
|
settings.conf theme, dark/light mode and language preference
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -382,7 +383,7 @@ German in both languages.
|
||||||
- [x] Unit 2 ("Aus aller Welt") – 19 interactive exercises (incl. Hangman)
|
- [x] Unit 2 ("Aus aller Welt") – 19 interactive exercises (incl. Hangman)
|
||||||
- [x] Unit 3 ("Bei uns zu Hause") – 15 interactive exercises
|
- [x] Unit 3 ("Bei uns zu Hause") – 15 interactive exercises
|
||||||
- [x] Computer networks units 1–10 (IP/VLSM + nine theory units with quizzes)
|
- [x] Computer networks units 1–10 (IP/VLSM + nine theory units with quizzes)
|
||||||
- [ ] Progress tracking / statistics for the networks path
|
- [x] Progress tracking / statistics for the networks path
|
||||||
- [ ] Lesson progression and unlock system for the remaining units
|
- [ ] Lesson progression and unlock system for the remaining units
|
||||||
- [ ] Add audio pronunciation features
|
- [ ] Add audio pronunciation features
|
||||||
- [x] Add user settings panel (theme, dark/light mode, language)
|
- [x] Add user settings panel (theme, dark/light mode, language)
|
||||||
|
|
@ -466,15 +467,15 @@ moderním GTK 4 rozhraním stylovaným přes CSS.
|
||||||
8. Přepojování – snímky + kvíz
|
8. Přepojování – snímky + kvíz
|
||||||
9. Vrstevnaté modely a protokoly – snímky + kvíz
|
9. Vrstevnaté modely a protokoly – snímky + kvíz
|
||||||
10. Model ISO/OSI a TCP/IP – snímky + kvíz
|
10. Model ISO/OSI a TCP/IP – snímky + kvíz
|
||||||
Postup u sítí se **zatím neukládá** (na rozdíl od Deutsch); ve Statistikách
|
Postup u sítí se ukládá do `progress/net.conf` (příznak na jednotku);
|
||||||
se sítě stále berou jako předmět bez trackovaných jednotek
|
dokončené jednotky mají fajfku na cestě a počítají se ve Statistikách
|
||||||
- **Ukládání postupu** – hotová cvičení zezelenají; stav se ukládá do
|
- **Ukládání postupu** – hotová cvičení zezelenají; stav se ukládá do
|
||||||
`progress/unit1.conf`, `progress/unit2.conf` a `progress/unit3.conf` a při
|
`progress/unit1.conf`, `progress/unit2.conf` a `progress/unit3.conf` a při
|
||||||
startu se načte zpět
|
startu se načte zpět
|
||||||
- **Statistiky** – ikona grafu v hlavičce ukáže postup po předmětech (součty,
|
- **Statistiky** – ikona grafu v hlavičce ukáže postup po předmětech (součty,
|
||||||
procenta, dokončené jednotky), progress bary u každého předmětu (předměty
|
procenta, dokončené jednotky), progress bary u každého předmětu (předměty
|
||||||
bez trackovaného obsahu — včetně sítí — jsou zatím jako zamčené) a rozpad
|
bez trackovaného obsahu jsou zamčené; **sítě** berou dokončení lekcí z
|
||||||
po jednotkách u Deutsch
|
`progress/net.conf`) a rozpad po jednotkách u Deutsch
|
||||||
- **Nastavení** – ozubené kolečko vpravo nahoře:
|
- **Nastavení** – ozubené kolečko vpravo nahoře:
|
||||||
- **režim**: tmavý / světlý
|
- **režim**: tmavý / světlý
|
||||||
- **téma**: 10 barevných palet
|
- **téma**: 10 barevných palet
|
||||||
|
|
@ -684,15 +685,16 @@ Po spuštění se zobrazí úvodní obrazovka. Odtud:
|
||||||
Jednotky 11–30 zůstávají zamčené.
|
Jednotky 11–30 zůstávají zamčené.
|
||||||
6. Tlačítkem **zpět** vlevo nahoře se vrátíte na předchozí obrazovku.
|
6. Tlačítkem **zpět** vlevo nahoře se vrátíte na předchozí obrazovku.
|
||||||
7. **Statistiky** (ikona grafu vpravo nahoře) ukazují postup po předmětech
|
7. **Statistiky** (ikona grafu vpravo nahoře) ukazují postup po předmětech
|
||||||
(u Deutsch i po jednotkách). Postup sítí se ve Statistikách zatím
|
(u Deutsch i po jednotkách). Dokončené síťové lekce se započítávají do
|
||||||
nesleduje.
|
součů předmětu.
|
||||||
8. **Nastavení** (ozubené kolečko) – tmavý/světlý režim, barevné téma nebo
|
8. **Nastavení** (ozubené kolečko) – tmavý/světlý režim, barevné téma nebo
|
||||||
jazyk rozhraní.
|
jazyk rozhraní.
|
||||||
|
|
||||||
Hotové jsou německé jednotky 1–3 a síťové jednotky 1–10. Zamčené uzly nic
|
Hotové jsou německé jednotky 1–3 a síťové jednotky 1–10. Zamčené uzly nic
|
||||||
nedělají – jsou to placeholdery. Dokončená německá cvičení zůstanou zelená a
|
nedělají – jsou to placeholdery. Dokončená německá cvičení zůstanou zelená a
|
||||||
ukládají se do `progress/unit1.conf` / `progress/unit2.conf` /
|
ukládají se do `progress/unit1.conf` / `progress/unit2.conf` /
|
||||||
`progress/unit3.conf` (vzniknou vedle aplikace při prvním dokončení).
|
`progress/unit3.conf`. Dokončené síťové jednotky se ukládají do
|
||||||
|
`progress/net.conf`.
|
||||||
|
|
||||||
#### Klávesové zkratky
|
#### Klávesové zkratky
|
||||||
|
|
||||||
|
|
@ -736,6 +738,7 @@ progress/ vzniká za běhu
|
||||||
unit1.conf stav cvičení jednotky 1
|
unit1.conf stav cvičení jednotky 1
|
||||||
unit2.conf stav cvičení jednotky 2
|
unit2.conf stav cvičení jednotky 2
|
||||||
unit3.conf stav cvičení jednotky 3
|
unit3.conf stav cvičení jednotky 3
|
||||||
|
net.conf dokončení síťových lekcí
|
||||||
settings.conf téma, režim a jazyk
|
settings.conf téma, režim a jazyk
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -768,7 +771,7 @@ rozhraní.
|
||||||
- [x] Jednotka 2 („Aus aller Welt“) – 19 cvičení (včetně Hangmana)
|
- [x] Jednotka 2 („Aus aller Welt“) – 19 cvičení (včetně Hangmana)
|
||||||
- [x] Jednotka 3 („Bei uns zu Hause“) – 15 cvičení
|
- [x] Jednotka 3 („Bei uns zu Hause“) – 15 cvičení
|
||||||
- [x] Počítačové sítě: jednotky 1–10 (IP/VLSM + 9 teoretických s kvízy)
|
- [x] Počítačové sítě: jednotky 1–10 (IP/VLSM + 9 teoretických s kvízy)
|
||||||
- [ ] Ukládání postupu / statistiky pro síťovou cestu
|
- [x] Ukládání postupu / statistiky pro síťovou cestu
|
||||||
- [ ] Postupné odemykání zbývajících jednotek
|
- [ ] Postupné odemykání zbývajících jednotek
|
||||||
- [ ] Audio / výslovnost
|
- [ ] Audio / výslovnost
|
||||||
- [x] Panel nastavení (téma, režim, jazyk)
|
- [x] Panel nastavení (téma, režim, jazyk)
|
||||||
|
|
|
||||||
21
i18n.c
21
i18n.c
|
|
@ -253,6 +253,27 @@ const TrEntry tr_ui[] = {
|
||||||
"3 slides about ISO/OSI and TCP/IP, then a quiz."},
|
"3 slides about ISO/OSI and TCP/IP, then a quiz."},
|
||||||
{"net_ex10_title", "Kvíz: ISO/OSI a TCP/IP",
|
{"net_ex10_title", "Kvíz: ISO/OSI a TCP/IP",
|
||||||
"Quiz: ISO/OSI and TCP/IP"},
|
"Quiz: ISO/OSI and TCP/IP"},
|
||||||
|
{"net_quiz_intro",
|
||||||
|
"Vyberte u každé otázky jednu správnou odpověď a stiskněte Zkontrolovat.",
|
||||||
|
"Choose one correct answer for each question and press Check."},
|
||||||
|
{"net_quiz2_head", "Kvíz k základním pojmům sítí",
|
||||||
|
"Quiz on basic networking concepts"},
|
||||||
|
{"net_quiz3_head", "Kvíz ke klient–server a cloudu",
|
||||||
|
"Quiz on client–server and cloud"},
|
||||||
|
{"net_quiz4_head", "Kvíz k přenosu dat a zabezpečení",
|
||||||
|
"Quiz on data transmission and security"},
|
||||||
|
{"net_quiz5_head", "Kvíz k signálům a modulaci",
|
||||||
|
"Quiz on signals and modulation"},
|
||||||
|
{"net_quiz6_head", "Kvíz k cestě, kanálu a okruhu",
|
||||||
|
"Quiz on path, channel and circuit"},
|
||||||
|
{"net_quiz7_head", "Kvíz k multiplexu a směrům přenosu",
|
||||||
|
"Quiz on multiplexing and transmission directions"},
|
||||||
|
{"net_quiz8_head", "Kvíz k přepojování",
|
||||||
|
"Quiz on switching"},
|
||||||
|
{"net_quiz9_head", "Kvíz k vrstevnatým modelům a protokolům",
|
||||||
|
"Quiz on layered models and protocols"},
|
||||||
|
{"net_quiz10_head", "Kvíz k ISO/OSI a TCP/IP",
|
||||||
|
"Quiz on ISO/OSI and TCP/IP"},
|
||||||
{"net_slide_next", "Další", "Next"},
|
{"net_slide_next", "Další", "Next"},
|
||||||
{"net_slide_prev", "Zpět", "Back"},
|
{"net_slide_prev", "Zpět", "Back"},
|
||||||
{"net_slide_start", "Přejít na cvičení", "Go to the exercise"},
|
{"net_slide_start", "Přejít na cvičení", "Go to the exercise"},
|
||||||
|
|
|
||||||
47
main.c
47
main.c
|
|
@ -171,26 +171,33 @@ void activate(GtkApplication *app, gpointer user_data) {
|
||||||
gtk_stack_add_named(main_stack, roadmap_page, "roadmap");
|
gtk_stack_add_named(main_stack, roadmap_page, "roadmap");
|
||||||
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_net_unit1_page(), "netunit1");
|
{
|
||||||
gtk_stack_add_named(main_stack, build_net_exercise_page(), "netex");
|
typedef GtkWidget *(*NetBuilder)(void);
|
||||||
gtk_stack_add_named(main_stack, build_net_unit2_page(), "netunit2");
|
static const struct {
|
||||||
gtk_stack_add_named(main_stack, build_net_unit2_exercise_page(), "netex2");
|
const char *unit_name;
|
||||||
gtk_stack_add_named(main_stack, build_net_unit3_page(), "netunit3");
|
const char *ex_name;
|
||||||
gtk_stack_add_named(main_stack, build_net_unit3_exercise_page(), "netex3");
|
NetBuilder build_unit;
|
||||||
gtk_stack_add_named(main_stack, build_net_unit4_page(), "netunit4");
|
NetBuilder build_ex;
|
||||||
gtk_stack_add_named(main_stack, build_net_unit4_exercise_page(), "netex4");
|
} net_pages[] = {
|
||||||
gtk_stack_add_named(main_stack, build_net_unit5_page(), "netunit5");
|
{"netunit1", "netex1", build_net_unit1_page, build_net_exercise_page},
|
||||||
gtk_stack_add_named(main_stack, build_net_unit5_exercise_page(), "netex5");
|
{"netunit2", "netex2", build_net_unit2_page, build_net_unit2_exercise_page},
|
||||||
gtk_stack_add_named(main_stack, build_net_unit6_page(), "netunit6");
|
{"netunit3", "netex3", build_net_unit3_page, build_net_unit3_exercise_page},
|
||||||
gtk_stack_add_named(main_stack, build_net_unit6_exercise_page(), "netex6");
|
{"netunit4", "netex4", build_net_unit4_page, build_net_unit4_exercise_page},
|
||||||
gtk_stack_add_named(main_stack, build_net_unit7_page(), "netunit7");
|
{"netunit5", "netex5", build_net_unit5_page, build_net_unit5_exercise_page},
|
||||||
gtk_stack_add_named(main_stack, build_net_unit7_exercise_page(), "netex7");
|
{"netunit6", "netex6", build_net_unit6_page, build_net_unit6_exercise_page},
|
||||||
gtk_stack_add_named(main_stack, build_net_unit8_page(), "netunit8");
|
{"netunit7", "netex7", build_net_unit7_page, build_net_unit7_exercise_page},
|
||||||
gtk_stack_add_named(main_stack, build_net_unit8_exercise_page(), "netex8");
|
{"netunit8", "netex8", build_net_unit8_page, build_net_unit8_exercise_page},
|
||||||
gtk_stack_add_named(main_stack, build_net_unit9_page(), "netunit9");
|
{"netunit9", "netex9", build_net_unit9_page, build_net_unit9_exercise_page},
|
||||||
gtk_stack_add_named(main_stack, build_net_unit9_exercise_page(), "netex9");
|
{"netunit10", "netex10", build_net_unit10_page, build_net_unit10_exercise_page},
|
||||||
gtk_stack_add_named(main_stack, build_net_unit10_page(), "netunit10");
|
};
|
||||||
gtk_stack_add_named(main_stack, build_net_unit10_exercise_page(), "netex10");
|
|
||||||
|
for (guint i = 0; i < G_N_ELEMENTS(net_pages); i++) {
|
||||||
|
gtk_stack_add_named(main_stack, net_pages[i].build_unit(),
|
||||||
|
net_pages[i].unit_name);
|
||||||
|
gtk_stack_add_named(main_stack, net_pages[i].build_ex(),
|
||||||
|
net_pages[i].ex_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < NUM_UNLOCKED; i++) {
|
for (int i = 0; i < NUM_UNLOCKED; i++) {
|
||||||
UnitCtx *u = &units[i];
|
UnitCtx *u = &units[i];
|
||||||
|
|
|
||||||
20
maturita.h
20
maturita.h
|
|
@ -18,6 +18,7 @@
|
||||||
#define PROGRESS_U2 "progress/unit2.conf"
|
#define PROGRESS_U2 "progress/unit2.conf"
|
||||||
#define PROGRESS_U3 "progress/unit3.conf"
|
#define PROGRESS_U3 "progress/unit3.conf"
|
||||||
#define SETTINGS_FILE "progress/settings.conf"
|
#define SETTINGS_FILE "progress/settings.conf"
|
||||||
|
#define PROGRESS_NET "progress/net.conf"
|
||||||
#define NODE_SIZE 88.0
|
#define NODE_SIZE 88.0
|
||||||
#define PATH_SPAC 240.0
|
#define PATH_SPAC 240.0
|
||||||
#define ROAD_MX 150.0 /* horizontal canvas margin */
|
#define ROAD_MX 150.0 /* horizontal canvas margin */
|
||||||
|
|
@ -44,7 +45,6 @@
|
||||||
#define HM_ROW3 19 /* start of the ZXCV row */
|
#define HM_ROW3 19 /* start of the ZXCV row */
|
||||||
#define HM_ROW4 26 /* start of the umlaut row */
|
#define HM_ROW4 26 /* start of the umlaut row */
|
||||||
#define NET_UNITS 30
|
#define NET_UNITS 30
|
||||||
#define NET_GRID_COLS 6
|
|
||||||
#define NET_SLIDES 4
|
#define NET_SLIDES 4
|
||||||
#define NET2_SLIDES 5
|
#define NET2_SLIDES 5
|
||||||
#define NET3_SLIDES 4
|
#define NET3_SLIDES 4
|
||||||
|
|
@ -487,6 +487,11 @@ typedef struct {
|
||||||
guint n_slides;
|
guint n_slides;
|
||||||
const char *unit_page;
|
const char *unit_page;
|
||||||
const char *ex_page;
|
const char *ex_page;
|
||||||
|
GArray *note_kicks;
|
||||||
|
GArray *note_titles;
|
||||||
|
GArray *note_bodies;
|
||||||
|
GtkWidget *done_icon;
|
||||||
|
gboolean done;
|
||||||
} NetLesson;
|
} NetLesson;
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *kicker;
|
const char *kicker;
|
||||||
|
|
@ -498,6 +503,7 @@ typedef struct {
|
||||||
const ChoiceQ *qs;
|
const ChoiceQ *qs;
|
||||||
int n;
|
int n;
|
||||||
int n_opts;
|
int n_opts;
|
||||||
|
int lesson_id;
|
||||||
GtkWidget *feedback;
|
GtkWidget *feedback;
|
||||||
GtkToggleButton **toggles;
|
GtkToggleButton **toggles;
|
||||||
GtkWidget **hints;
|
GtkWidget **hints;
|
||||||
|
|
@ -663,9 +669,6 @@ extern const U3Kw ex14_u3_qs[];
|
||||||
extern const U3Let ex15_u3_rows[];
|
extern const U3Let ex15_u3_rows[];
|
||||||
extern NetLesson net_lessons[NET_LESSONS];
|
extern NetLesson net_lessons[NET_LESSONS];
|
||||||
extern GtkWidget *net_note_host;
|
extern GtkWidget *net_note_host;
|
||||||
extern GArray *net_note_kicks;
|
|
||||||
extern GArray *net_note_titles;
|
|
||||||
extern GArray *net_note_bodies;
|
|
||||||
extern int net_note_last_w;
|
extern int net_note_last_w;
|
||||||
extern GtkWidget *net_scroll;
|
extern GtkWidget *net_scroll;
|
||||||
extern GtkWidget *net_fixed;
|
extern GtkWidget *net_fixed;
|
||||||
|
|
@ -700,7 +703,6 @@ void i18n_apply_one(const I18nBind *b);
|
||||||
void i18n_bind(GtkWidget *widget, const char *key, int kind);
|
void i18n_bind(GtkWidget *widget, const char *key, int kind);
|
||||||
void refresh_welcome_heading(void);
|
void refresh_welcome_heading(void);
|
||||||
void refresh_stats_ui(void);
|
void refresh_stats_ui(void);
|
||||||
void net_lessons_apply_lang(void);
|
|
||||||
void net_rail_theme_reset(void);
|
void net_rail_theme_reset(void);
|
||||||
void apply_language(void);
|
void apply_language(void);
|
||||||
char *read_css_file(void);
|
char *read_css_file(void);
|
||||||
|
|
@ -738,6 +740,11 @@ void load_progress(void);
|
||||||
void all_rails_redraw(void);
|
void all_rails_redraw(void);
|
||||||
void refresh_completion_ui(void);
|
void refresh_completion_ui(void);
|
||||||
void mark_done(UnitCtx *u, int n);
|
void mark_done(UnitCtx *u, int n);
|
||||||
|
void net_load_progress(void);
|
||||||
|
void net_save_progress(void);
|
||||||
|
void mark_net_done(int lesson_id);
|
||||||
|
void refresh_net_completion_ui(void);
|
||||||
|
void progress_for_net(ProgressSum *out);
|
||||||
GtkWidget *make_back_button(const char *target);
|
GtkWidget *make_back_button(const char *target);
|
||||||
void on_nav_clicked(GtkButton *button, gpointer user_data);
|
void on_nav_clicked(GtkButton *button, gpointer user_data);
|
||||||
int unit_done_count(const UnitCtx *u);
|
int unit_done_count(const UnitCtx *u);
|
||||||
|
|
@ -1035,6 +1042,9 @@ void net_qz_header(GtkWidget *box, const char *text);
|
||||||
GtkWidget *net_qz_make_entry(void);
|
GtkWidget *net_qz_make_entry(void);
|
||||||
GtkWidget *build_net_exercise_page(void);
|
GtkWidget *build_net_exercise_page(void);
|
||||||
void net_mcq_check(GtkButton *button, gpointer data);
|
void net_mcq_check(GtkButton *button, gpointer data);
|
||||||
|
GtkWidget *build_net_mcq_page(int lesson_id, const char *back_page,
|
||||||
|
const char *title_key, const char *heading_key,
|
||||||
|
const ChoiceQ *qs, const char **hints, int n);
|
||||||
GtkWidget *build_net_unit2_exercise_page(void);
|
GtkWidget *build_net_unit2_exercise_page(void);
|
||||||
GtkWidget *build_net_unit3_exercise_page(void);
|
GtkWidget *build_net_unit3_exercise_page(void);
|
||||||
GtkWidget *build_net_unit4_exercise_page(void);
|
GtkWidget *build_net_unit4_exercise_page(void);
|
||||||
|
|
|
||||||
93
progress.c
93
progress.c
|
|
@ -52,6 +52,7 @@ void unit_load_progress(UnitCtx *u) {
|
||||||
void load_progress(void) {
|
void load_progress(void) {
|
||||||
for (int i = 0; i < NUM_UNLOCKED; i++)
|
for (int i = 0; i < NUM_UNLOCKED; i++)
|
||||||
unit_load_progress(&units[i]);
|
unit_load_progress(&units[i]);
|
||||||
|
net_load_progress();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Redraw the exercise rails of every unit that already has a page. */
|
/* Redraw the exercise rails of every unit that already has a page. */
|
||||||
|
|
@ -122,6 +123,7 @@ void refresh_completion_ui(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
all_rails_redraw();
|
all_rails_redraw();
|
||||||
|
refresh_net_completion_ui();
|
||||||
refresh_stats_ui();
|
refresh_stats_ui();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,3 +134,94 @@ void mark_done(UnitCtx *u, int n) {
|
||||||
refresh_completion_ui();
|
refresh_completion_ui();
|
||||||
unit_save_progress(u);
|
unit_save_progress(u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void net_load_progress(void) {
|
||||||
|
GKeyFile *kf = g_key_file_new();
|
||||||
|
GError *err = NULL;
|
||||||
|
|
||||||
|
if (!g_key_file_load_from_file(kf, PROGRESS_NET, G_KEY_FILE_NONE, &err)) {
|
||||||
|
if (err)
|
||||||
|
g_error_free(err);
|
||||||
|
g_key_file_free(kf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < NET_LESSONS; i++) {
|
||||||
|
gchar key[8];
|
||||||
|
|
||||||
|
g_snprintf(key, sizeof(key), "%d", i + 1);
|
||||||
|
net_lessons[i].done = g_key_file_get_boolean(kf, "done", key, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_key_file_free(kf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void net_save_progress(void) {
|
||||||
|
GKeyFile *kf = g_key_file_new();
|
||||||
|
|
||||||
|
for (int i = 0; i < NET_LESSONS; i++) {
|
||||||
|
gchar key[8];
|
||||||
|
|
||||||
|
g_snprintf(key, sizeof(key), "%d", i + 1);
|
||||||
|
g_key_file_set_boolean(kf, "done", key, net_lessons[i].done);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_mkdir_with_parents(PROGRESS_DIR, 0755) != 0) {
|
||||||
|
g_key_file_free(kf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gchar *data = g_key_file_to_data(kf, NULL, NULL);
|
||||||
|
g_key_file_free(kf);
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
GError *err = NULL;
|
||||||
|
g_file_set_contents(PROGRESS_NET, data, -1, &err);
|
||||||
|
if (err)
|
||||||
|
g_error_free(err);
|
||||||
|
g_free(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void refresh_net_completion_ui(void) {
|
||||||
|
for (int i = 0; i < NET_LESSONS; i++) {
|
||||||
|
GtkWidget *node = net_nodes[i];
|
||||||
|
GtkWidget *icon = net_lessons[i].done_icon;
|
||||||
|
|
||||||
|
if (!node)
|
||||||
|
continue;
|
||||||
|
if (net_lessons[i].done) {
|
||||||
|
gtk_widget_remove_css_class(node, "current");
|
||||||
|
gtk_widget_add_css_class(node, "done");
|
||||||
|
if (icon)
|
||||||
|
gtk_widget_set_visible(icon, TRUE);
|
||||||
|
} else {
|
||||||
|
gtk_widget_remove_css_class(node, "done");
|
||||||
|
gtk_widget_add_css_class(node, "current");
|
||||||
|
if (icon)
|
||||||
|
gtk_widget_set_visible(icon, FALSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mark_net_done(int lesson_id) {
|
||||||
|
if (lesson_id < 0 || lesson_id >= NET_LESSONS)
|
||||||
|
return;
|
||||||
|
if (net_lessons[lesson_id].done)
|
||||||
|
return;
|
||||||
|
net_lessons[lesson_id].done = TRUE;
|
||||||
|
net_save_progress();
|
||||||
|
refresh_net_completion_ui();
|
||||||
|
refresh_stats_ui();
|
||||||
|
}
|
||||||
|
|
||||||
|
void progress_for_net(ProgressSum *out) {
|
||||||
|
out->total_ex += NET_LESSONS;
|
||||||
|
out->open_units += NET_LESSONS;
|
||||||
|
for (int i = 0; i < NET_LESSONS; i++) {
|
||||||
|
if (net_lessons[i].done) {
|
||||||
|
out->done_ex++;
|
||||||
|
out->done_units++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
21
stats.c
21
stats.c
|
|
@ -53,8 +53,12 @@ void refresh_stats_ui(void) {
|
||||||
if (!stats_ui.ex_value)
|
if (!stats_ui.ex_value)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (int s = 0; s < NUM_SUBJECTS; s++)
|
for (int s = 0; s < NUM_SUBJECTS; s++) {
|
||||||
progress_for_units(sub_unit_start[s], sub_unit_count[s], &all);
|
if (s == NET_SUBJ)
|
||||||
|
progress_for_net(&all);
|
||||||
|
else
|
||||||
|
progress_for_units(sub_unit_start[s], sub_unit_count[s], &all);
|
||||||
|
}
|
||||||
|
|
||||||
pct = all.total_ex > 0 ? (all.done_ex * 100) / all.total_ex : 0;
|
pct = all.total_ex > 0 ? (all.done_ex * 100) / all.total_ex : 0;
|
||||||
|
|
||||||
|
|
@ -79,11 +83,15 @@ void refresh_stats_ui(void) {
|
||||||
GtkWidget *count = stats_ui.subj[s].count;
|
GtkWidget *count = stats_ui.subj[s].count;
|
||||||
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) || sub_unit_count[s] > 0;
|
||||||
|
|
||||||
if (!count || !bar)
|
if (!count || !bar)
|
||||||
continue;
|
continue;
|
||||||
progress_for_units(sub_unit_start[s], sub_unit_count[s], &sp);
|
if (s == NET_SUBJ)
|
||||||
if (sub_unit_count[s] > 0) {
|
progress_for_net(&sp);
|
||||||
|
else
|
||||||
|
progress_for_units(sub_unit_start[s], sub_unit_count[s], &sp);
|
||||||
|
if (has_content) {
|
||||||
tmp = g_strdup_printf(tr("stats_ex_fmt"), sp.done_ex, sp.total_ex);
|
tmp = g_strdup_printf(tr("stats_ex_fmt"), sp.done_ex, sp.total_ex);
|
||||||
gtk_label_set_text(GTK_LABEL(count), tmp);
|
gtk_label_set_text(GTK_LABEL(count), tmp);
|
||||||
g_free(tmp);
|
g_free(tmp);
|
||||||
|
|
@ -345,7 +353,8 @@ GtkWidget *build_stats_page(void) {
|
||||||
GtkWidget *name;
|
GtkWidget *name;
|
||||||
GtkWidget *count;
|
GtkWidget *count;
|
||||||
GtkWidget *bar;
|
GtkWidget *bar;
|
||||||
gboolean has_units = sub_unit_count[s] > 0;
|
gboolean has_units = (s == NET_SUBJ) || 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);
|
||||||
gtk_widget_add_css_class(subject, "stats-subject");
|
gtk_widget_add_css_class(subject, "stats-subject");
|
||||||
|
|
@ -379,7 +388,7 @@ GtkWidget *build_stats_page(void) {
|
||||||
gtk_box_append(GTK_BOX(subject), bar);
|
gtk_box_append(GTK_BOX(subject), bar);
|
||||||
stats_ui.subj[s].bar = bar;
|
stats_ui.subj[s].bar = bar;
|
||||||
|
|
||||||
if (has_units) {
|
if (has_deutsch_units) {
|
||||||
GtkWidget *units_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
GtkWidget *units_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
||||||
|
|
||||||
gtk_widget_add_css_class(units_box, "stats-units");
|
gtk_widget_add_css_class(units_box, "stats-units");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue