From b50835fcd5c3c05f55fb5de6300d62060386000a Mon Sep 17 00:00:00 2001 From: pepaskopec-blip Date: Sun, 11 Oct 2026 13:05:00 +0200 Subject: [PATCH 1/7] Refresh the README screenshots for the current Mac app. The old shots still showed the previous welcome screen and only four open subjects. Co-authored-by: Cursor --- README.md | 28 ++++++++++++++-------------- assets/deutsch.png | Bin 0 -> 136728 bytes assets/germanlection.png | Bin 56056 -> 0 bytes assets/home.png | Bin 0 -> 152697 bytes assets/neue-freunde.png | Bin 0 -> 201374 bytes assets/subjectsscreen.png | Bin 47718 -> 0 bytes assets/welcomescreen.png | Bin 61755 -> 0 bytes 7 files changed, 14 insertions(+), 14 deletions(-) create mode 100644 assets/deutsch.png delete mode 100644 assets/germanlection.png create mode 100644 assets/home.png create mode 100644 assets/neue-freunde.png delete mode 100644 assets/subjectsscreen.png delete mode 100644 assets/welcomescreen.png diff --git a/README.md b/README.md index a57339b..2b8f907 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,9 @@ Vzdělávací aplikace k přípravě na maturitu (repo `Graduately`). / An educa Rozhraní je v češtině a angličtině. Otevřené předměty jsou v seznamu níže; ostatní jsou zatím zamčené. Aplikace se po instalaci aktualizuje sama. -| Úvod | Předměty | Německá cesta | -| ---- | -------- | ------------- | -| ![Úvod](assets/welcomescreen.png) | ![Předměty](assets/subjectsscreen.png) | ![Německá cesta](assets/germanlection.png) | +| Úvod | Němčina | Neue Freunde | +| ---- | -------- | ------------ | +| ![Úvod](assets/home.png) | ![Němčina](assets/deutsch.png) | ![Neue Freunde](assets/neue-freunde.png) | ### Instalace @@ -90,10 +90,10 @@ Intel Mac, jiná architektura nebo úpravy kódu → [sestavení ze zdroje](#ses ### Použití -1. **Pokračuj** otevře mapu předmětů. -2. Otevřený předmět (vlajka, Wi‑Fi, čip, česká vlajka, váhy, funkce, atom, baňka) vede na učební cestu. -3. Uzly cvičení / lekcí otevřou obsah; zamčené nic nedělají. -4. **Hledat** (lupa), **Statistiky** (graf) a **Nastavení** (ozubené kolo) jsou vpravo nahoře. +1. **Úvod** ukáže postup a mřížku předmětů. Otevřený předmět spustí cestu. Zamčené dlaždice zatím nic neotevřou: technická grafika, prezentační grafika, programování a programové vybavení. +2. U němčiny je učebnice (Neue Freunde, Aus aller Welt, Bei uns zu Hause) a čtyři ročníky. Ostatní otevřené předměty mají ročníky, lekce nebo knihy. +3. Z cesty se otevře výklad, poslech, čtení, psaní nebo kvíz. +4. **Statistiky**, **Nastavení** a **Hledat** jsou v postranním panelu na Macu. Na telefonu a v desktopové aplikaci jsou na záložkách, hledání navíc ikonou lupy. Zkratka je `Cmd/Ctrl+K`. ### Sestavení ze zdroje @@ -198,9 +198,9 @@ Licence: [GPL-3.0](LICENSE). The UI is Czech and English. Open subjects are listed below; the rest are still locked. After install the app updates itself. -| Welcome | Subjects | German path | -| ------- | -------- | ----------- | -| ![Welcome](assets/welcomescreen.png) | ![Subjects](assets/subjectsscreen.png) | ![German path](assets/germanlection.png) | +| Welcome | German | Neue Freunde | +| ------- | ------ | ----------- | +| ![Welcome](assets/home.png) | ![German](assets/deutsch.png) | ![Neue Freunde](assets/neue-freunde.png) | ### Install @@ -279,10 +279,10 @@ Intel Mac, another architecture, or hacking on the code → ### Usage -1. **Continue** opens the subject map. -2. An open subject (flag, Wi‑Fi, chip, Czech flag, scales, function, atom, flask) opens its path. -3. Exercise / lesson nodes open content; locked nodes do nothing. -4. **Search** (magnifier), **Statistics** (chart) and **Settings** (gear) sit in the top-right. +1. **Welcome** shows progress and the subject grid. An open subject starts its path. Locked tiles do nothing yet: technical drawing, presentation graphics, programming and software. +2. German has the textbook (Neue Freunde, Aus aller Welt, Bei uns zu Hause) and four years. The other open subjects have years, lessons or books. +3. A path opens an overview, listening, reading, writing or a quiz. +4. **Statistics**, **Settings** and **Search** sit in the sidebar on Mac. On the phone and the desktop app they are tabs, with search also on the magnifier. The shortcut is `Cmd/Ctrl+K`. ### Build from source diff --git a/assets/deutsch.png b/assets/deutsch.png new file mode 100644 index 0000000000000000000000000000000000000000..91ed3e61f83f60aefedfd9fa5923172332003545 GIT binary patch literal 136728 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Μ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°y)y4Q@TE`TcYjbY?LE0g}l#%KcF@r4J%?Y;B! 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 literal 0 HcmV?d00001 diff --git a/assets/germanlection.png b/assets/germanlection.png deleted file mode 100644 index ab5fd6fd379507ee12e3bc705d4f0c8ca3c83efe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56056 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 z 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 D6EoqH literal 0 HcmV?d00001 diff --git a/assets/neue-freunde.png b/assets/neue-freunde.png new file mode 100644 index 0000000000000000000000000000000000000000..3c10067483dd2dfedd43ae76bddefef62fa8c769 GIT binary patch literal 201374 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 H?EU{AIiC+E literal 0 HcmV?d00001 diff --git a/assets/subjectsscreen.png b/assets/subjectsscreen.png deleted file mode 100644 index 6c09b7529f47f9b954b26c36ffb5c4fff0c83fc3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47718 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 I_sgCC0pyk^;s5{u diff --git a/assets/welcomescreen.png b/assets/welcomescreen.png deleted file mode 100644 index 9edb54e96eaef25a53cfdd9e5f4f2a6e969de85b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61755 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 From 6662eb185e40792ea11a41cb0375c5b24d263872 Mon Sep 17 00:00:00 2001 From: pepaskopec-blip Date: Sun, 11 Oct 2026 13:44:38 +0200 Subject: [PATCH 2/7] Open technical graphics as four years of AutoCAD. Each lesson has a method, a dimensioned assignment and a quiz, from the drawing sheet through the exam model. Co-authored-by: Cursor --- README.md | 8 +- .../shared/src/org/maturita/maturita/App.kt | 59 ++++++++ .../src/org/maturita/maturita/AppViewModel.kt | 20 +++ .../shared/src/org/maturita/maturita/Route.kt | 55 +++++++ .../src/org/maturita/maturita/data/Content.kt | 8 ++ .../maturita/maturita/data/ProgressStore.kt | 40 ++++++ .../src/org/maturita/maturita/ui/Exercises.kt | 24 ++++ .../src/org/maturita/maturita/ui/Screens.kt | 44 ++++++ content/README.md | 4 +- content/aplikace/novinky.txt | 6 + content/aplikace/predmety.md | 2 +- content/aplikace/texty.md | 16 ++- .../1-rocnik/01-format-a-meritko.en.md | 6 + .../1-rocnik/01-format-a-meritko.md | 80 +++++++++++ .../1-rocnik/02-druhy-car.en.md | 6 + .../1-rocnik/02-druhy-car.md | 80 +++++++++++ .../1-rocnik/03-prostredi-autocadu.en.md | 6 + .../1-rocnik/03-prostredi-autocadu.md | 80 +++++++++++ .../1-rocnik/04-souradnice.en.md | 6 + .../1-rocnik/04-souradnice.md | 80 +++++++++++ .../1-rocnik/05-zakladni-entity.en.md | 6 + .../1-rocnik/05-zakladni-entity.md | 80 +++++++++++ .../1-rocnik/06-uchopeni-a-orto.en.md | 6 + .../1-rocnik/06-uchopeni-a-orto.md | 80 +++++++++++ .../1-rocnik/07-posun-kopie-otoceni.en.md | 6 + .../1-rocnik/07-posun-kopie-otoceni.md | 80 +++++++++++ .../1-rocnik/08-zadani-desticka.en.md | 6 + .../1-rocnik/08-zadani-desticka.md | 80 +++++++++++ .../2-rocnik/01-orez-a-ekvidistanta.en.md | 6 + .../2-rocnik/01-orez-a-ekvidistanta.md | 80 +++++++++++ .../2-rocnik/02-zaobleni-a-zkoseni.en.md | 6 + .../2-rocnik/02-zaobleni-a-zkoseni.md | 80 +++++++++++ .../2-rocnik/03-hladiny.en.md | 6 + .../technicka-grafika/2-rocnik/03-hladiny.md | 80 +++++++++++ .../technicka-grafika/2-rocnik/04-srafy.en.md | 6 + .../technicka-grafika/2-rocnik/04-srafy.md | 80 +++++++++++ .../2-rocnik/05-koty-linearni.en.md | 6 + .../2-rocnik/05-koty-linearni.md | 80 +++++++++++ .../2-rocnik/06-koty-prumeru-a-uhlu.en.md | 6 + .../2-rocnik/06-koty-prumeru-a-uhlu.md | 80 +++++++++++ .../technicka-grafika/2-rocnik/07-pole.en.md | 6 + content/technicka-grafika/2-rocnik/07-pole.md | 80 +++++++++++ .../2-rocnik/08-zadani-priruba.en.md | 6 + .../2-rocnik/08-zadani-priruba.md | 80 +++++++++++ .../3-rocnik/01-pohledy.en.md | 6 + .../technicka-grafika/3-rocnik/01-pohledy.md | 80 +++++++++++ .../technicka-grafika/3-rocnik/02-rezy.en.md | 6 + content/technicka-grafika/3-rocnik/02-rezy.md | 80 +++++++++++ .../technicka-grafika/3-rocnik/03-bloky.en.md | 6 + .../technicka-grafika/3-rocnik/03-bloky.md | 80 +++++++++++ .../3-rocnik/04-atributy-a-kusovnik.en.md | 6 + .../3-rocnik/04-atributy-a-kusovnik.md | 80 +++++++++++ .../3-rocnik/05-rozvrzeni-a-vyrezy.en.md | 6 + .../3-rocnik/05-rozvrzeni-a-vyrezy.md | 80 +++++++++++ .../technicka-grafika/3-rocnik/06-tisk.en.md | 6 + content/technicka-grafika/3-rocnik/06-tisk.md | 80 +++++++++++ .../3-rocnik/07-izometrie.en.md | 6 + .../3-rocnik/07-izometrie.md | 80 +++++++++++ .../3-rocnik/08-zadani-pouzdro.en.md | 6 + .../3-rocnik/08-zadani-pouzdro.md | 80 +++++++++++ .../4-rocnik/01-prostor-3d.en.md | 6 + .../4-rocnik/01-prostor-3d.md | 80 +++++++++++ .../4-rocnik/02-telesa-a-vysunuti.en.md | 6 + .../4-rocnik/02-telesa-a-vysunuti.md | 80 +++++++++++ .../4-rocnik/03-rotace.en.md | 6 + .../technicka-grafika/4-rocnik/03-rotace.md | 80 +++++++++++ .../4-rocnik/04-boolean.en.md | 6 + .../technicka-grafika/4-rocnik/04-boolean.md | 80 +++++++++++ .../4-rocnik/05-rez-a-flatshot.en.md | 6 + .../4-rocnik/05-rez-a-flatshot.md | 80 +++++++++++ .../4-rocnik/06-zobrazeni.en.md | 6 + .../4-rocnik/06-zobrazeni.md | 80 +++++++++++ .../4-rocnik/07-vykres-z-modelu.en.md | 6 + .../4-rocnik/07-vykres-z-modelu.md | 80 +++++++++++ .../4-rocnik/08-maturitni-zadani.en.md | 6 + .../4-rocnik/08-maturitni-zadani.md | 80 +++++++++++ ios/Graduately/AppModel.swift | 20 +++ ios/Graduately/Data/Content.swift | 12 ++ ios/Graduately/Data/ProgressStore.swift | 40 ++++++ ios/Graduately/Route.swift | 81 +++++++++++ ios/Graduately/UI/Components.swift | 1 + ios/Graduately/UI/ExercisesMore.swift | 33 +++++ ios/Graduately/UI/Screens.swift | 134 ++++++++++++++++++ scripts/build-content.py | 2 + 84 files changed, 3355 insertions(+), 6 deletions(-) create mode 100644 content/technicka-grafika/1-rocnik/01-format-a-meritko.en.md create mode 100644 content/technicka-grafika/1-rocnik/01-format-a-meritko.md create mode 100644 content/technicka-grafika/1-rocnik/02-druhy-car.en.md create mode 100644 content/technicka-grafika/1-rocnik/02-druhy-car.md create mode 100644 content/technicka-grafika/1-rocnik/03-prostredi-autocadu.en.md create mode 100644 content/technicka-grafika/1-rocnik/03-prostredi-autocadu.md create mode 100644 content/technicka-grafika/1-rocnik/04-souradnice.en.md create mode 100644 content/technicka-grafika/1-rocnik/04-souradnice.md create mode 100644 content/technicka-grafika/1-rocnik/05-zakladni-entity.en.md create mode 100644 content/technicka-grafika/1-rocnik/05-zakladni-entity.md create mode 100644 content/technicka-grafika/1-rocnik/06-uchopeni-a-orto.en.md create mode 100644 content/technicka-grafika/1-rocnik/06-uchopeni-a-orto.md create mode 100644 content/technicka-grafika/1-rocnik/07-posun-kopie-otoceni.en.md create mode 100644 content/technicka-grafika/1-rocnik/07-posun-kopie-otoceni.md create mode 100644 content/technicka-grafika/1-rocnik/08-zadani-desticka.en.md create mode 100644 content/technicka-grafika/1-rocnik/08-zadani-desticka.md create mode 100644 content/technicka-grafika/2-rocnik/01-orez-a-ekvidistanta.en.md create mode 100644 content/technicka-grafika/2-rocnik/01-orez-a-ekvidistanta.md create mode 100644 content/technicka-grafika/2-rocnik/02-zaobleni-a-zkoseni.en.md create mode 100644 content/technicka-grafika/2-rocnik/02-zaobleni-a-zkoseni.md create mode 100644 content/technicka-grafika/2-rocnik/03-hladiny.en.md create mode 100644 content/technicka-grafika/2-rocnik/03-hladiny.md create mode 100644 content/technicka-grafika/2-rocnik/04-srafy.en.md create mode 100644 content/technicka-grafika/2-rocnik/04-srafy.md create mode 100644 content/technicka-grafika/2-rocnik/05-koty-linearni.en.md create mode 100644 content/technicka-grafika/2-rocnik/05-koty-linearni.md create mode 100644 content/technicka-grafika/2-rocnik/06-koty-prumeru-a-uhlu.en.md create mode 100644 content/technicka-grafika/2-rocnik/06-koty-prumeru-a-uhlu.md create mode 100644 content/technicka-grafika/2-rocnik/07-pole.en.md create mode 100644 content/technicka-grafika/2-rocnik/07-pole.md create mode 100644 content/technicka-grafika/2-rocnik/08-zadani-priruba.en.md create mode 100644 content/technicka-grafika/2-rocnik/08-zadani-priruba.md create mode 100644 content/technicka-grafika/3-rocnik/01-pohledy.en.md create mode 100644 content/technicka-grafika/3-rocnik/01-pohledy.md create mode 100644 content/technicka-grafika/3-rocnik/02-rezy.en.md create mode 100644 content/technicka-grafika/3-rocnik/02-rezy.md create mode 100644 content/technicka-grafika/3-rocnik/03-bloky.en.md create mode 100644 content/technicka-grafika/3-rocnik/03-bloky.md create mode 100644 content/technicka-grafika/3-rocnik/04-atributy-a-kusovnik.en.md create mode 100644 content/technicka-grafika/3-rocnik/04-atributy-a-kusovnik.md create mode 100644 content/technicka-grafika/3-rocnik/05-rozvrzeni-a-vyrezy.en.md create mode 100644 content/technicka-grafika/3-rocnik/05-rozvrzeni-a-vyrezy.md create mode 100644 content/technicka-grafika/3-rocnik/06-tisk.en.md create mode 100644 content/technicka-grafika/3-rocnik/06-tisk.md create mode 100644 content/technicka-grafika/3-rocnik/07-izometrie.en.md create mode 100644 content/technicka-grafika/3-rocnik/07-izometrie.md create mode 100644 content/technicka-grafika/3-rocnik/08-zadani-pouzdro.en.md create mode 100644 content/technicka-grafika/3-rocnik/08-zadani-pouzdro.md create mode 100644 content/technicka-grafika/4-rocnik/01-prostor-3d.en.md create mode 100644 content/technicka-grafika/4-rocnik/01-prostor-3d.md create mode 100644 content/technicka-grafika/4-rocnik/02-telesa-a-vysunuti.en.md create mode 100644 content/technicka-grafika/4-rocnik/02-telesa-a-vysunuti.md create mode 100644 content/technicka-grafika/4-rocnik/03-rotace.en.md create mode 100644 content/technicka-grafika/4-rocnik/03-rotace.md create mode 100644 content/technicka-grafika/4-rocnik/04-boolean.en.md create mode 100644 content/technicka-grafika/4-rocnik/04-boolean.md create mode 100644 content/technicka-grafika/4-rocnik/05-rez-a-flatshot.en.md create mode 100644 content/technicka-grafika/4-rocnik/05-rez-a-flatshot.md create mode 100644 content/technicka-grafika/4-rocnik/06-zobrazeni.en.md create mode 100644 content/technicka-grafika/4-rocnik/06-zobrazeni.md create mode 100644 content/technicka-grafika/4-rocnik/07-vykres-z-modelu.en.md create mode 100644 content/technicka-grafika/4-rocnik/07-vykres-z-modelu.md create mode 100644 content/technicka-grafika/4-rocnik/08-maturitni-zadani.en.md create mode 100644 content/technicka-grafika/4-rocnik/08-maturitni-zadani.md diff --git a/README.md b/README.md index 2b8f907..db629d9 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,8 @@ Intel Mac, jiná architektura nebo úpravy kódu → [sestavení ze zdroje](#ses - **Technické vybavení** — 4 ročníky: 1. ročník má 29 lekcí (architektura, data, skříň, deska, karty, disky a vstup), ročníky 2–4 po 8 lekcích (monitory, sestavení, RAID, servery, údržba) +- **Technická grafika** — 4 ročníky AutoCADu, v každém 8 lekcí: + výkres a souřadnice, úpravy a kóty, pohledy a tisk, 3D a maturitní zadání - **Český jazyk a literatura** — literatura (4 ročníky, 32 lekcí podle osnov středních škol), mluvnice (20 cvičení) a maturitní četba (82 knih: zápisky, kvíz, sestavení děje) @@ -90,7 +92,7 @@ Intel Mac, jiná architektura nebo úpravy kódu → [sestavení ze zdroje](#ses ### Použití -1. **Úvod** ukáže postup a mřížku předmětů. Otevřený předmět spustí cestu. Zamčené dlaždice zatím nic neotevřou: technická grafika, prezentační grafika, programování a programové vybavení. +1. **Úvod** ukáže postup a mřížku předmětů. Otevřený předmět spustí cestu. Zamčené dlaždice zatím nic neotevřou: prezentační grafika, programování a programové vybavení. 2. U němčiny je učebnice (Neue Freunde, Aus aller Welt, Bei uns zu Hause) a čtyři ročníky. Ostatní otevřené předměty mají ročníky, lekce nebo knihy. 3. Z cesty se otevře výklad, poslech, čtení, psaní nebo kvíz. 4. **Statistiky**, **Nastavení** a **Hledat** jsou v postranním panelu na Macu. Na telefonu a v desktopové aplikaci jsou na záložkách, hledání navíc ikonou lupy. Zkratka je `Cmd/Ctrl+K`. @@ -259,6 +261,8 @@ Intel Mac, another architecture, or hacking on the code → - **Computer hardware** — 4 years: year 1 has 29 lessons (architecture, data, case, board, cards, drives and input), years 2–4 have 8 lessons each (displays, assembly, RAID, servers, maintenance) +- **Technical graphics** — 4 years of AutoCAD, 8 lessons each: + the sheet and coordinates, editing and dimensions, views and plotting, 3D and the exam drawing - **Czech language** — literature (4 years, 32 lessons from the secondary-school syllabus), grammar (20 exercises) and required reading (82 books: notes, quiz, plot ordering) @@ -279,7 +283,7 @@ Intel Mac, another architecture, or hacking on the code → ### Usage -1. **Welcome** shows progress and the subject grid. An open subject starts its path. Locked tiles do nothing yet: technical drawing, presentation graphics, programming and software. +1. **Welcome** shows progress and the subject grid. An open subject starts its path. Locked tiles do nothing yet: presentation graphics, programming and software. 2. German has the textbook (Neue Freunde, Aus aller Welt, Bei uns zu Hause) and four years. The other open subjects have years, lessons or books. 3. A path opens an overview, listening, reading, writing or a quiz. 4. **Statistics**, **Settings** and **Search** sit in the sidebar on Mac. On the phone and the desktop app they are tabs, with search also on the magnifier. The shortcut is `Cmd/Ctrl+K`. diff --git a/android/shared/src/org/maturita/maturita/App.kt b/android/shared/src/org/maturita/maturita/App.kt index 7f796e1..4f2839a 100644 --- a/android/shared/src/org/maturita/maturita/App.kt +++ b/android/shared/src/org/maturita/maturita/App.kt @@ -30,6 +30,8 @@ import org.maturita.maturita.ui.HwQuizScreen import org.maturita.maturita.ui.HwYearsScreen import org.maturita.maturita.ui.LessonListScreen import org.maturita.maturita.ui.FyzQuizScreen +import org.maturita.maturita.ui.TgQuizScreen +import org.maturita.maturita.ui.TgYearsScreen import org.maturita.maturita.ui.MathPracticeScreen import org.maturita.maturita.ui.MatYearsScreen import org.maturita.maturita.ui.FyzYearsScreen @@ -525,6 +527,63 @@ private fun MaturitaAppBody(vm: AppViewModel) { } } is Route.Fyz4Ex -> FyzQuizScreen(vm, 4, r.id) + Route.TgYears -> TgYearsScreen(vm) + Route.TgMap -> LessonListScreen( + vm, vm.tr("tg_year1"), vm.tr("tg_sub"), + vm.content.tg.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.tgDone(id), Route.TgLesson(id)) + }, + ) + is Route.TgLesson -> { + val l = vm.content.tgLesson(r.id) + if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) { + vm.replace(Route.TgEx(r.id)) + } + } + is Route.TgEx -> TgQuizScreen(vm, 1, r.id) + Route.Tg2Map -> LessonListScreen( + vm, vm.tr("tg_year2"), vm.tr("tg2_sub"), + vm.content.tg2.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.tg2Done(id), Route.Tg2Lesson(id)) + }, + ) + is Route.Tg2Lesson -> { + val l = vm.content.tg2Lesson(r.id) + if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) { + vm.replace(Route.Tg2Ex(r.id)) + } + } + is Route.Tg2Ex -> TgQuizScreen(vm, 2, r.id) + Route.Tg3Map -> LessonListScreen( + vm, vm.tr("tg_year3"), vm.tr("tg3_sub"), + vm.content.tg3.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.tg3Done(id), Route.Tg3Lesson(id)) + }, + ) + is Route.Tg3Lesson -> { + val l = vm.content.tg3Lesson(r.id) + if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) { + vm.replace(Route.Tg3Ex(r.id)) + } + } + is Route.Tg3Ex -> TgQuizScreen(vm, 3, r.id) + Route.Tg4Map -> LessonListScreen( + vm, vm.tr("tg_year4"), vm.tr("tg4_sub"), + vm.content.tg4.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.tg4Done(id), Route.Tg4Lesson(id)) + }, + ) + is Route.Tg4Lesson -> { + val l = vm.content.tg4Lesson(r.id) + if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) { + vm.replace(Route.Tg4Ex(r.id)) + } + } + is Route.Tg4Ex -> TgQuizScreen(vm, 4, r.id) Route.SciMap -> SciMapScreen(vm) Route.ChemMap -> LessonListScreen( vm, vm.tr("Chemie"), vm.tr("chem_sub"), diff --git a/android/shared/src/org/maturita/maturita/AppViewModel.kt b/android/shared/src/org/maturita/maturita/AppViewModel.kt index 03fd3ea..956f666 100644 --- a/android/shared/src/org/maturita/maturita/AppViewModel.kt +++ b/android/shared/src/org/maturita/maturita/AppViewModel.kt @@ -242,6 +242,26 @@ class AppViewModel(val content: Content, val platform: Platform) { refresh() } + fun markTg(id: Int) { + progress.markTg(id) + refresh() + } + + fun markTg2(id: Int) { + progress.markTg2(id) + refresh() + } + + fun markTg3(id: Int) { + progress.markTg3(id) + refresh() + } + + fun markTg4(id: Int) { + progress.markTg4(id) + refresh() + } + fun markMluv(n: Int) { progress.markMluv(n) refresh() diff --git a/android/shared/src/org/maturita/maturita/Route.kt b/android/shared/src/org/maturita/maturita/Route.kt index c90685e..1e6cdf8 100644 --- a/android/shared/src/org/maturita/maturita/Route.kt +++ b/android/shared/src/org/maturita/maturita/Route.kt @@ -105,6 +105,19 @@ sealed class Route(val page: String) { data object Fyz4Map : Route("fyz4map") data class Fyz4Lesson(val id: Int) : Route("fyz4unit$id") data class Fyz4Ex(val id: Int) : Route("fyz4ex$id") + data object TgYears : Route("tgyears") + data object TgMap : Route("tgmap") + data class TgLesson(val id: Int) : Route("tgunit$id") + data class TgEx(val id: Int) : Route("tgex$id") + data object Tg2Map : Route("tg2map") + data class Tg2Lesson(val id: Int) : Route("tg2unit$id") + data class Tg2Ex(val id: Int) : Route("tg2ex$id") + data object Tg3Map : Route("tg3map") + data class Tg3Lesson(val id: Int) : Route("tg3unit$id") + data class Tg3Ex(val id: Int) : Route("tg3ex$id") + data object Tg4Map : Route("tg4map") + data class Tg4Lesson(val id: Int) : Route("tg4unit$id") + data class Tg4Ex(val id: Int) : Route("tg4ex$id") data object CzechMap : Route("czechmap") data object Mluvnice : Route("mluvnice") data class MluvEx(val n: Int) : Route("mluve$n") @@ -237,6 +250,19 @@ sealed class Route(val page: String) { Fyz4Map -> listOf(Subjects, FyzYears, Fyz4Map) is Fyz4Lesson -> listOf(Subjects, FyzYears, Fyz4Map, this) is Fyz4Ex -> listOf(Subjects, FyzYears, Fyz4Map, Fyz4Lesson(id), this) + TgYears -> listOf(Subjects, TgYears) + TgMap -> listOf(Subjects, TgYears, TgMap) + is TgLesson -> listOf(Subjects, TgYears, TgMap, this) + is TgEx -> listOf(Subjects, TgYears, TgMap, TgLesson(id), this) + Tg2Map -> listOf(Subjects, TgYears, Tg2Map) + is Tg2Lesson -> listOf(Subjects, TgYears, Tg2Map, this) + is Tg2Ex -> listOf(Subjects, TgYears, Tg2Map, Tg2Lesson(id), this) + Tg3Map -> listOf(Subjects, TgYears, Tg3Map) + is Tg3Lesson -> listOf(Subjects, TgYears, Tg3Map, this) + is Tg3Ex -> listOf(Subjects, TgYears, Tg3Map, Tg3Lesson(id), this) + Tg4Map -> listOf(Subjects, TgYears, Tg4Map) + is Tg4Lesson -> listOf(Subjects, TgYears, Tg4Map, this) + is Tg4Ex -> listOf(Subjects, TgYears, Tg4Map, Tg4Lesson(id), this) CzechMap -> listOf(Subjects, CzechMap) Mluvnice -> listOf(Subjects, CzechMap, Mluvnice) is MluvEx -> listOf(Subjects, CzechMap, Mluvnice, this) @@ -289,6 +315,11 @@ fun routeFromPage(page: String): Route? = when (page) { "fyz2map" -> Route.Fyz2Map "fyz3map" -> Route.Fyz3Map "fyz4map" -> Route.Fyz4Map + "tgyears" -> Route.TgYears + "tgmap" -> Route.TgMap + "tg2map" -> Route.Tg2Map + "tg3map" -> Route.Tg3Map + "tg4map" -> Route.Tg4Map "czechmap" -> Route.CzechMap "mluvnice" -> Route.Mluvnice "readinglist" -> Route.ReadingList @@ -386,6 +417,30 @@ fun routeFromPage(page: String): Route? = when (page) { Regex("""fyzex(\d+)""").matchEntire(page)?.let { return Route.FyzEx(it.groupValues[1].toInt()) } + Regex("""tg4unit(\d+)""").matchEntire(page)?.let { + return Route.Tg4Lesson(it.groupValues[1].toInt()) + } + Regex("""tg4ex(\d+)""").matchEntire(page)?.let { + return Route.Tg4Ex(it.groupValues[1].toInt()) + } + Regex("""tg3unit(\d+)""").matchEntire(page)?.let { + return Route.Tg3Lesson(it.groupValues[1].toInt()) + } + Regex("""tg3ex(\d+)""").matchEntire(page)?.let { + return Route.Tg3Ex(it.groupValues[1].toInt()) + } + Regex("""tg2unit(\d+)""").matchEntire(page)?.let { + return Route.Tg2Lesson(it.groupValues[1].toInt()) + } + Regex("""tg2ex(\d+)""").matchEntire(page)?.let { + return Route.Tg2Ex(it.groupValues[1].toInt()) + } + Regex("""tgunit(\d+)""").matchEntire(page)?.let { + return Route.TgLesson(it.groupValues[1].toInt()) + } + Regex("""tgex(\d+)""").matchEntire(page)?.let { + return Route.TgEx(it.groupValues[1].toInt()) + } Regex("""lit4unit(\d+)""").matchEntire(page)?.let { return Route.Lit4Lesson(it.groupValues[1].toInt()) } diff --git a/android/shared/src/org/maturita/maturita/data/Content.kt b/android/shared/src/org/maturita/maturita/data/Content.kt index 44176ee..5a82ac7 100644 --- a/android/shared/src/org/maturita/maturita/data/Content.kt +++ b/android/shared/src/org/maturita/maturita/data/Content.kt @@ -56,6 +56,10 @@ class Content(root: JSONObject) { val fyz2 = raw.arr("fyz2") val fyz3 = raw.arr("fyz3") val fyz4 = raw.arr("fyz4") + val tg = raw.arr("tg") + val tg2 = raw.arr("tg2") + val tg3 = raw.arr("tg3") + val tg4 = raw.arr("tg4") val mat0 = raw.arr("mat0") val mat = raw.arr("mat") val mat2 = raw.arr("mat2") @@ -108,6 +112,10 @@ class Content(root: JSONObject) { fun fyz2Lesson(id: Int) = fyz2.firstOrNull { it.int("id") == id } fun fyz3Lesson(id: Int) = fyz3.firstOrNull { it.int("id") == id } fun fyz4Lesson(id: Int) = fyz4.firstOrNull { it.int("id") == id } + fun tgLesson(id: Int) = tg.firstOrNull { it.int("id") == id } + fun tg2Lesson(id: Int) = tg2.firstOrNull { it.int("id") == id } + fun tg3Lesson(id: Int) = tg3.firstOrNull { it.int("id") == id } + fun tg4Lesson(id: Int) = tg4.firstOrNull { it.int("id") == id } fun mat0Lesson(id: Int) = mat0.firstOrNull { it.int("id") == id } fun matLesson(id: Int) = mat.firstOrNull { it.int("id") == id } fun mat2Lesson(id: Int) = mat2.firstOrNull { it.int("id") == id } diff --git a/android/shared/src/org/maturita/maturita/data/ProgressStore.kt b/android/shared/src/org/maturita/maturita/data/ProgressStore.kt index 67ad66b..1c29a6f 100644 --- a/android/shared/src/org/maturita/maturita/data/ProgressStore.kt +++ b/android/shared/src/org/maturita/maturita/data/ProgressStore.kt @@ -91,6 +91,14 @@ class ProgressStore(private val prefs: Prefs, content: Content) { fun markFyz3(id: Int) { mark("fyz3.$id") } fun fyz4Done(id: Int) = done("fyz4.$id") fun markFyz4(id: Int) { mark("fyz4.$id") } + fun tgDone(id: Int) = done("tg.$id") + fun markTg(id: Int) { mark("tg.$id") } + fun tg2Done(id: Int) = done("tg2.$id") + fun markTg2(id: Int) { mark("tg2.$id") } + fun tg3Done(id: Int) = done("tg3.$id") + fun markTg3(id: Int) { mark("tg3.$id") } + fun tg4Done(id: Int) = done("tg4.$id") + fun markTg4(id: Int) { mark("tg4.$id") } fun mat0Done(id: Int) = done("mat0.$id") fun markMat0(id: Int) { mark("mat0.$id") } fun matDone(id: Int) = done("mat.$id") @@ -280,6 +288,38 @@ fun summarize(content: Content, p: ProgressStore): ProgressSum { sum.doneUnits += 1 } } + content.tg.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.tgDone(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.tg2.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.tg2Done(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.tg3.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.tg3Done(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.tg4.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.tg4Done(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } content.mat0.forEach { l -> sum.totalEx += 1 sum.openUnits += 1 diff --git a/android/shared/src/org/maturita/maturita/ui/Exercises.kt b/android/shared/src/org/maturita/maturita/ui/Exercises.kt index fb9cabe..8fcddd6 100644 --- a/android/shared/src/org/maturita/maturita/ui/Exercises.kt +++ b/android/shared/src/org/maturita/maturita/ui/Exercises.kt @@ -1082,6 +1082,30 @@ fun ChoiceLessonScreen(vm: AppViewModel, lesson: J?, onDone: () -> Unit) { DispatchExercise(vm, spec, vm.tr(lesson.str("exTitleKey")), onDone) } +@Composable +fun TgQuizScreen(vm: AppViewModel, year: Int, id: Int) { + val lesson = when (year) { + 2 -> vm.content.tg2Lesson(id) + 3 -> vm.content.tg3Lesson(id) + 4 -> vm.content.tg4Lesson(id) + else -> vm.content.tgLesson(id) + } ?: return + val spec = J(org.json.JSONObject().apply { + put("type", "choice") + put("title", vm.tr(lesson.str("exTitleKey"))) + put("sub", lesson.str("quizHeadKey")) + put("questions", lesson.o.optJSONArray("quiz")) + }) + DispatchExercise(vm, spec, vm.tr(lesson.str("exTitleKey"))) { + when (year) { + 2 -> vm.markTg2(id) + 3 -> vm.markTg3(id) + 4 -> vm.markTg4(id) + else -> vm.markTg(id) + } + } +} + @Composable fun FyzQuizScreen(vm: AppViewModel, year: Int, id: Int) { val lesson = when (year) { diff --git a/android/shared/src/org/maturita/maturita/ui/Screens.kt b/android/shared/src/org/maturita/maturita/ui/Screens.kt index 1a4e87c..bb4be71 100644 --- a/android/shared/src/org/maturita/maturita/ui/Screens.kt +++ b/android/shared/src/org/maturita/maturita/ui/Screens.kt @@ -25,6 +25,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.MenuBook +import androidx.compose.material.icons.filled.Architecture import androidx.compose.material.icons.filled.Bolt import androidx.compose.material.icons.filled.Functions import androidx.compose.material.icons.filled.Check @@ -225,6 +226,7 @@ private fun SubjectIcon(icon: String, open: Boolean) { "flask" -> Icon(Icons.Filled.Science, null, tint = tint, modifier = Modifier.size(26.dp)) "bolt" -> Icon(Icons.Filled.Bolt, null, tint = tint, modifier = Modifier.size(26.dp)) "function" -> Icon(Icons.Filled.Functions, null, tint = tint, modifier = Modifier.size(26.dp)) + "ruler" -> Icon(Icons.Filled.Architecture, null, tint = tint, modifier = Modifier.size(26.dp)) else -> Icon(Icons.Filled.Lock, null, tint = tint) } } @@ -348,6 +350,13 @@ private fun subjectSum(vm: AppViewModel, s: J): ProgressSum { vm.content.fyz3.count { vm.progress.fyz3Done(it.int("id")) } + vm.content.fyz4.count { vm.progress.fyz4Done(it.int("id")) } } + "tgyears", "tgmap", "tg2map", "tg3map", "tg4map" -> { + sum.totalEx = vm.content.tg.size + vm.content.tg2.size + vm.content.tg3.size + vm.content.tg4.size + sum.doneEx = vm.content.tg.count { vm.progress.tgDone(it.int("id")) } + + vm.content.tg2.count { vm.progress.tg2Done(it.int("id")) } + + vm.content.tg3.count { vm.progress.tg3Done(it.int("id")) } + + vm.content.tg4.count { vm.progress.tg4Done(it.int("id")) } + } "czechmap" -> { val litDone = vm.content.lit.count { vm.progress.litDone(it.int("id")) } + vm.content.lit2.count { vm.progress.lit2Done(it.int("id")) } + @@ -649,6 +658,27 @@ private fun buildSearch(vm: AppViewModel): List { add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "fyz4unit${l.int("id")}", extra = "fyzika") add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "fyz4ex${l.int("id")}") } + add(vm.tr("Technická grafika"), vm.tr("search_lesson"), "tgyears", extra = "autocad grafika") + add(vm.tr("tg_year1"), vm.tr("search_lesson"), "tgmap", extra = "rocnik") + add(vm.tr("tg_year2"), vm.tr("search_lesson"), "tg2map", extra = "rocnik") + add(vm.tr("tg_year3"), vm.tr("search_lesson"), "tg3map", extra = "rocnik") + add(vm.tr("tg_year4"), vm.tr("search_lesson"), "tg4map", extra = "rocnik") + vm.content.tg.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "tgunit${l.int("id")}", extra = "autocad") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tgex${l.int("id")}") + } + vm.content.tg2.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "tg2unit${l.int("id")}", extra = "autocad") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tg2ex${l.int("id")}") + } + vm.content.tg3.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "tg3unit${l.int("id")}", extra = "autocad") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tg3ex${l.int("id")}") + } + vm.content.tg4.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "tg4unit${l.int("id")}", extra = "autocad") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tg4ex${l.int("id")}") + } add(vm.tr("Chemie"), vm.tr("search_lesson"), "chemmap", extra = "chemie") add(vm.tr("Biologie"), vm.tr("search_lesson"), "biomap", extra = "biologie") vm.content.chem.forEach { l -> @@ -848,6 +878,20 @@ fun MatYearsScreen(vm: AppViewModel) { } } +@Composable +fun TgYearsScreen(vm: AppViewModel) { + DetailScaffold(vm, vm.tr("Technická grafika"), vm.tr("tg_years_sub")) { inner -> + LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { + item { + NavRow(title = vm.tr("tg_year1"), subtitle = vm.tr("tg_sub"), leading = { Text("1.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.TgMap) } + NavRow(title = vm.tr("tg_year2"), subtitle = vm.tr("tg2_sub"), leading = { Text("2.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Tg2Map) } + NavRow(title = vm.tr("tg_year3"), subtitle = vm.tr("tg3_sub"), leading = { Text("3.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Tg3Map) } + NavRow(title = vm.tr("tg_year4"), subtitle = vm.tr("tg4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Tg4Map) } + } + } + } +} + @Composable fun FyzYearsScreen(vm: AppViewModel) { DetailScaffold(vm, vm.tr("Fyzika"), vm.tr("fyz_years_sub")) { inner -> diff --git a/content/README.md b/content/README.md index ce160ef..da047be 100644 --- a/content/README.md +++ b/content/README.md @@ -18,7 +18,7 @@ Skript vypíše počet lekcí, nebo chyby s cestou a číslem řádku | Složka | Obsah | |---|---| -| `pocitacove-site/`, `technicke-vybaveni/`, `obcanska-nauka/`, `fyzika/` | `N-rocnik/`, v nich lekce | +| `pocitacove-site/`, `technicke-vybaveni/`, `technicka-grafika/`, `obcanska-nauka/`, `fyzika/` | `N-rocnik/`, v nich lekce | | `prirodni-vedy/chemie`, `prirodni-vedy/biologie` | lekce | | `cestina/literatura/N-rocnik` | lekce literatury | | `cestina/mluvnice` | cvičení z mluvnice | @@ -97,7 +97,7 @@ a smažte `puvodni`. ## Běžná lekce -Sítě, hardware, občanka, literatura, chemie, biologie, fyzika. +Sítě, hardware, technická grafika, občanka, literatura, chemie, biologie, fyzika. ```md --- diff --git a/content/aplikace/novinky.txt b/content/aplikace/novinky.txt index 1f6aef9..fb941b4 100644 --- a/content/aplikace/novinky.txt +++ b/content/aplikace/novinky.txt @@ -5,6 +5,12 @@ # cs: bullet # en: bullet +--- 2026-10-11 +cs: Technická grafika: čtyři ročníky AutoCADu, od výkresu po 3D +cs: V každé lekci je postup, zadání s rozměry a kvíz +en: Technical graphics: four years of AutoCAD, from the drawing sheet to 3D +en: Every lesson has a method, a dimensioned assignment and a quiz + --- 2026-10-07 cs: Sítě a technické vybavení: doplněné 2. až 4. ročníky, v každém 8 lekcí cs: Výklad a kvíz od VLAN a sestavení počítače po maturitu diff --git a/content/aplikace/predmety.md b/content/aplikace/predmety.md index 39592ae..9266e4e 100644 --- a/content/aplikace/predmety.md +++ b/content/aplikace/predmety.md @@ -11,7 +11,7 @@ | Matematika | Matematika | Mathematics | function | matyears | ano | | Fyzika | Fyzika | Physics | bolt | fyzyears | ano | | Základy přírodních věd | Základy přírodních věd | Fundamentals of Natural Sciences | flask | scimap | ano | -| Technická grafika | Technická grafika | Technical Drawing | lock | | ne | +| Technická grafika | Technická grafika | Technical Graphics | ruler | tgyears | ano | | Prezentační grafika | Prezentační grafika | Presentation Graphics | lock | | ne | | Programování | Programování | Programming | lock | | ne | | Programové vybavení | Programové vybavení | Software | lock | | ne | diff --git a/content/aplikace/texty.md b/content/aplikace/texty.md index 089a5b0..d445708 100644 --- a/content/aplikace/texty.md +++ b/content/aplikace/texty.md @@ -40,7 +40,7 @@ |---|---|---| | welcome_kicker | Graduately | Graduately | | welcome_title | Maturita?\nTo dáme. | Maturita?\nWe've got this. | -| welcome_body | Procvičování k maturitě v jednom okně. Česky i anglicky, napsané v C studenty ze SSŠVT. | Exam practice in one window. Czech and English, written in C by students from SSŠVT. | +| welcome_body | Procvičování k maturitě v jednom okně. Česky i anglicky, napsané studenty ze SSŠVT. | Exam practice in one window. Czech and English, written by students from SSŠVT. | | welcome_body_android | Procvičování k maturitě v telefonu. Česky i anglicky, stejný obsah jako na počítači. | Exam practice on your phone. Czech and English, the same content as on the computer. | | welcome_body_desktop | Procvičování k maturitě na počítači. Česky i anglicky, stejný obsah jako v telefonu. | Exam practice on your computer. Czech and English, the same content as on your phone. | | welcome_body_ios | Nativní procvičování na iPhonu. Česky i anglicky, stejný obsah, rozhraní podle iOS. | Native exam practice on iPhone. Czech and English, the same content, adapted to iOS. | @@ -229,6 +229,20 @@ | fyz3_sub | Elektřina a magnetismus. | Electricity and magnetism. | | fyz4_sub | Optika, relativita, mikročástice a vesmír. | Optics, relativity, the micro-world and the universe. | +# Technická grafika + +| Klíč | Česky | Anglicky | +|---|---|---| +| tg_years_sub | Čtyři ročníky AutoCADu, od výkresu po maturitní zadání. | Four years of AutoCAD, from the drawing sheet to the exam project. | +| tg_year1 | 1. ročník | Year 1 | +| tg_year2 | 2. ročník | Year 2 | +| tg_year3 | 3. ročník | Year 3 | +| tg_year4 | 4. ročník | Year 4 | +| tg_sub | Výkres, souřadnice a první příkazy. | The sheet, coordinates and the first commands. | +| tg2_sub | Úpravy, hladiny, šrafy a kóty. | Editing, layers, hatches and dimensions. | +| tg3_sub | Pohledy, řezy, bloky a tisk. | Views, sections, blocks and plotting. | +| tg4_sub | 3D model a výkres k maturitě. | A 3D model and the exam drawing. | + # Angličtina a němčina | Klíč | Česky | Anglicky | diff --git a/content/technicka-grafika/1-rocnik/01-format-a-meritko.en.md b/content/technicka-grafika/1-rocnik/01-format-a-meritko.en.md new file mode 100644 index 0000000..2de20d7 --- /dev/null +++ b/content/technicka-grafika/1-rocnik/01-format-a-meritko.en.md @@ -0,0 +1,6 @@ +--- +nazev: Sheet size, scale and the title block +popis: A sizes, drawing scale and the title block. +nazev_cviceni: Quiz: sheet and scale +nadpis_kvizu: Check the sheet, the scale and the title block +--- diff --git a/content/technicka-grafika/1-rocnik/01-format-a-meritko.md b/content/technicka-grafika/1-rocnik/01-format-a-meritko.md new file mode 100644 index 0000000..2ef74ba --- /dev/null +++ b/content/technicka-grafika/1-rocnik/01-format-a-meritko.md @@ -0,0 +1,80 @@ +--- +id: tg-1-format-a-meritko +nazev: Formát, měřítko a razítko +popis: Řada A, měřítko výkresu a popisové pole. +nazev_cviceni: Kvíz: formát a měřítko +nadpis_kvizu: Ověřte si formát, měřítko a razítko +--- + +# Výklad + +## Řada formátů A +*Papír* + +- Základní formát A4 má 210 × 297 mm. Delší strana je na výšku. +- A3 vznikne složením dvou A4: 297 × 420 mm. +- A2 je 420 × 594 mm. Každý větší formát má dvojnásobnou plochu. +- Na škole se součást kreslí nejčastěji na A4 nebo A3. + +> Tip: kratší strana většího formátu je delší stranou menšího. A3 je na šířku, A4 na výšku. + +## Co znamená měřítko +*Měřítko* + +- Měřítko 1:1 znamená, že výkres je ve skutečné velikosti. +- Měřítko 1:2 zmenšuje kresbu na polovinu. Měřítko 2:1 ji zvětšuje na dvojnásobek. +- Kóta se vždy píše ve skutečném rozměru, ne v rozměru nakreslené čáry. +- Do razítka se měřítko zapíše tak, jak se kreslilo, třeba 1:2. + +> Tip: díra o průměru 20 mm má na výkresu 1:2 čáru dlouhou 10 mm, ale kóta říká 20. + +## Popisové pole +*Razítko* + +- Razítko je v pravém dolním rohu. Čte se zprava, když je výkres ve správné poloze. +- Patří do něj název, měřítko, materiál, datum a jméno. +- Číslo výkresu a název součásti se nesmí vyměnit za ozdobný text. +- Rámeček je od okraje papíru odsazený, na školním A4 obvykle o 5 až 10 mm. + +> Tip: než začnete kreslit součást, vyhraďte si hladinu pro rámeček a razítko. + +## Zadání: rámeček A4 +*Cvičení* + +- Nakreslete obdélník 210 × 297 a dovnitř rámeček odsazený o 10 mm od každého okraje. +- Vpravo dole vyhraďte razítko 180 × 40 mm, které lícuje s vnitřním rámečkem. +- Do razítka vepište název Destička, měřítko 1:1 a své jméno. +- Soubor uložte jako A4-ramecek.dwg, jednotky milimetry. + +> Tip: vnitřní rámeček má 190 × 277 mm. Levý dolní roh dejte do bodu 0,0. + + +# Kvíz + +## Jaké rozměry má formát A4? +- 297 × 420 mm +- [x] 210 × 297 mm +- 420 × 594 mm +- 148 × 210 mm +> A3 je 297 × 420 mm. A4 je polovina této plochy, 210 × 297 mm. + +## Co se stane s kótou na výkresu v měřítku 1:2? +- Píše se polovina skutečného rozměru +- [x] Píše se skutečný rozměr +- Kóty se vynechají +- Měřítko se do razítka nepíše +> Kresba je poloviční, číslo na kótě zůstává skutečné. Měřítko 1:2 je v razítku. + +## Kam se na výkresu umísťuje razítko? +- Doprostřed plochy +- [x] Do pravého dolního rohu +- Nad součást, vlevo +- Mimo rámeček +> Razítko lícuje s vnitřním rámečkem vpravo dole, aby šlo výkres složit a razítko zůstalo navrchu. + +## Jak je velký vnitřní rámeček A4 při odsazení 10 mm? +- 210 × 297 mm +- [x] 190 × 277 mm +- 200 × 287 mm +- 180 × 40 mm +> Od 210 i od 297 se odečte 10 mm z každé strany, tedy 20 mm. 190 × 277 mm. diff --git a/content/technicka-grafika/1-rocnik/02-druhy-car.en.md b/content/technicka-grafika/1-rocnik/02-druhy-car.en.md new file mode 100644 index 0000000..afc6ade --- /dev/null +++ b/content/technicka-grafika/1-rocnik/02-druhy-car.en.md @@ -0,0 +1,6 @@ +--- +nazev: Line types and line weights +popis: Outlines, hidden edges, centrelines and dimensions. +nazev_cviceni: Quiz: lines +nadpis_kvizu: Name the line +--- diff --git a/content/technicka-grafika/1-rocnik/02-druhy-car.md b/content/technicka-grafika/1-rocnik/02-druhy-car.md new file mode 100644 index 0000000..542e176 --- /dev/null +++ b/content/technicka-grafika/1-rocnik/02-druhy-car.md @@ -0,0 +1,80 @@ +--- +id: tg-1-druhy-car +nazev: Druhy a tloušťky čar +popis: Obrys, skryté hrany, osy a kóty podle normy. +nazev_cviceni: Kvíz: čáry +nadpis_kvizu: Poznejte druh čáry +--- + +# Výklad + +## Tlustá plná čára +*Obrys* + +- Viditelné hrany a obrysy se kreslí souvislou tlustou čarou. +- V AutoCADu jí patří vlastní hladina, třeba OBRYS, s tloušťkou 0,5 mm. +- Tloušťka se nastavuje na hladině, ne u každé čáry zvlášť. +- Obrys nesmí být stejně tenký jako kóta, jinak se výkres nedá číst. + +> Tip: hladinu OBRYS nastavte jako aktuální dřív, než začnete kreslit tvar. + +## Tenké čáry +*Kóty a hrany* + +- Kóty, odkazy, šrafy a pomocné čáry jsou souvislé a tenké, kolem 0,25 mm. +- Skryté hrany, které z daného pohledu nejsou vidět, jsou čárkované a tenké. +- Osa díry nebo válce je čerchovaná: dlouhá čárka, tečka, dlouhá čárka. +- Osa přesahuje obrys jen o kousek, nepřetahuje se přes celý výkres. + +> Tip: čárkovaná čára je skrytá hrana. Čerchovaná je osa. Nejsou totéž. + +## Jak to nastavit v AutoCADu +*Hladiny* + +- Příkaz `_LAYER` otevře správce hladin. Založte OBRYS, SKRYTE, OSA a KOTA. +- U SKRYTE zvolte typ čáry DASHED, u OSA typ CENTER. +- Typ čáry se načítá příkazem `_LINETYPE`. Bez načtení ho hladina nenabídne. +- Měřítko typu čáry `_LTSCALE` zvětší nebo zmenší mezery v čárkování, ať je čára čitelná. + +> Tip: když čárkování vypadá jako plná čára, je LTSCALE moc velké nebo moc malé. + +## Zadání: tři čáry jedné díry +*Cvičení* + +- Nakreslete obdélník 60 × 40 tlustou plnou čarou. +- Svislou osu veďte středem a nechte ji přesáhnout obrys o 3 mm na každou stranu. +- Jednu zadní hranu, která z pohledu není vidět, nakreslete tenkou čárkovanou čarou. +- Každý druh čáry musí ležet na své hladině. + +> Tip: osa dlouhá 46 mm při výšce tělesa 40 mm přesahuje o 3 mm nahoře i dole. + + +# Kvíz + +## Jakou čarou se kreslí viditelný obrys? +- Tenkou čárkovanou +- [x] Tlustou plnou +- Tenkou čerchovanou +- Tlustou čerchovanou +> Viditelné hrany jsou souvislé a tlustší než kóty, osy i skryté hrany. + +## Co znamená tenká čárkovaná čára? +- Osu díry +- [x] Skrytou hranu +- Obrys +- Šrafu +> Čárkovaná čára je hrana, kterou v daném pohledu nevidíme. Osa je čerchovaná. + +## Jakou čarou se kreslí osa válce? +- Tlustou plnou +- Tenkou čárkovanou +- [x] Tenkou čerchovanou +- Vlnovkou +> Osa je dlouhá čárka, tečka, dlouhá čára. V AutoCADu je to typ CENTER. + +## Proč má mít obrys vlastní hladinu? +- Aby šel smazat jen razítko +- [x] Aby měla tloušťka a typ čáry jedno místo +- Hladiny v AutoCADu nejsou +- Obrys se kreslí jen od ruky +> Tloušťka i typ se nastaví na hladině. Všechny obrysy je pak mají stejné. diff --git a/content/technicka-grafika/1-rocnik/03-prostredi-autocadu.en.md b/content/technicka-grafika/1-rocnik/03-prostredi-autocadu.en.md new file mode 100644 index 0000000..2d39a07 --- /dev/null +++ b/content/technicka-grafika/1-rocnik/03-prostredi-autocadu.en.md @@ -0,0 +1,6 @@ +--- +nazev: The AutoCAD workspace +popis: Model space, the command line, units and limits. +nazev_cviceni: Quiz: the workspace +nadpis_kvizu: Check the first drawing setup +--- diff --git a/content/technicka-grafika/1-rocnik/03-prostredi-autocadu.md b/content/technicka-grafika/1-rocnik/03-prostredi-autocadu.md new file mode 100644 index 0000000..7a16b82 --- /dev/null +++ b/content/technicka-grafika/1-rocnik/03-prostredi-autocadu.md @@ -0,0 +1,80 @@ +--- +id: tg-1-prostredi +nazev: Prostředí AutoCADu +popis: Model, příkazový řádek, jednotky a meze. +nazev_cviceni: Kvíz: prostředí +nadpis_kvizu: Ověřte si první nastavení výkresu +--- + +# Výklad + +## Kam se kreslí +*Model* + +- Součást se kreslí v modelu. Papír, rámeček a výřezy patří až do rozvržení. +- Příkazový řádek je dole. Čte se pořád: AutoCAD se ptá na další bod. +- Stisk Enter nebo mezerník zopakuje poslední příkaz. +- Esc příkaz zruší. Neukládejte rozpracovaný příkaz klávesou Esc uprostřed bodu. + +> Tip: když AutoCAD „nic nedělá“, podívejte se, na co se příkazový řádek ptá. + +## Příkaz, který funguje vždy +*Podtržítko* + +- Český AutoCAD má přeložené příkazy. Úsečka se jmenuje ÚSEČKA. +- Mezinárodní jméno se píše s podtržítkem: `_LINE`. Funguje v každé jazykové verzi. +- Na pásu karet je stejný příkaz pod ikonou. Ikona i text dělají totéž. +- V těchto lekcích je u každého kroku mezinárodní příkaz. Naučte se ho psát. + +> Tip: `_LINE` a ÚSEČKA spustí stejné kreslení. Podtržítko se hodí, když čeština na klávesnici zlobí. + +## Jednotky a meze +*Milimetry* + +- `_UNITS` nastavte na desetinné milimetry. Úhly nechte ve stupních. +- Přesnost 0 stačí na celé milimetry, 0,0 když budete potřebovat desetinu. +- `_LIMITS` určí levý dolní a pravý horní roh kreslicí plochy, třeba 0,0 a 420,297. +- `_ZOOM` a volba All ukáže celé meze. Kolečko myši přibližuje ke kurzoru. + +> Tip: nový výkres začněte jednotkami. Přepočet z palců na milimetry uprostřed práce nic nespraví. + +## Zadání: prázdný výkres A3 +*Cvičení* + +- Založte soubor v milimetrech a nastavte meze 0,0 až 420,297. +- Příkazem `_RECTANG` nakreslete obrys papíru z bodu 0,0 do bodu 420,297. +- Přibližte vše příkazem `_ZOOM` a volbou Extents. +- Uložte soubor jako A3-prazdny.dwg. + +> Tip: Extents přiblíží to, co je nakreslené. All přiblíží meze, i když jsou prázdné. + + +# Kvíz + +## Kde se kreslí tvar součásti? +- V rozvržení, přes razítko +- [x] V modelu +- Jen na pásu karet +- V příkazu PLOT +> Model je prostor součásti. Rozvržení je papír, na který se model později vloží výřezem. + +## Co udělá příkaz `_LINE` v českém AutoCADu? +- Nastaví jednotky +- [x] Spustí kreslení úsečky +- Smaže poslední čáru +- Otevře hladiny +> Podtržítko volá mezinárodní příkaz. `_LINE` je úsečka, ať je AutoCAD v jakémkoli jazyce. + +## Jakou jednotku má školní výkres? +- Palec +- [x] Milimetr +- Metr +- Pixel +> `_UNITS` nastavte na desetinné milimetry dřív, než zadáte první rozměr. + +## Co ukáže `_ZOOM` s volbou Extents? +- Celé nastavené meze, i prázdné +- [x] Všechno, co je nakreslené +- Jen razítko +- Poslední příkaz +> Extents orámuje nakreslené objekty. All ukáže meze výkresu. diff --git a/content/technicka-grafika/1-rocnik/04-souradnice.en.md b/content/technicka-grafika/1-rocnik/04-souradnice.en.md new file mode 100644 index 0000000..95648e1 --- /dev/null +++ b/content/technicka-grafika/1-rocnik/04-souradnice.en.md @@ -0,0 +1,6 @@ +--- +nazev: Coordinates +popis: Absolute points, relative moves and polar entry. +nazev_cviceni: Quiz: coordinates +nadpis_kvizu: Find the next point +--- diff --git a/content/technicka-grafika/1-rocnik/04-souradnice.md b/content/technicka-grafika/1-rocnik/04-souradnice.md new file mode 100644 index 0000000..626ba0c --- /dev/null +++ b/content/technicka-grafika/1-rocnik/04-souradnice.md @@ -0,0 +1,80 @@ +--- +id: tg-1-souradnice +nazev: Souřadnice +popis: Absolutní bod, relativní posun a polární zápis. +nazev_cviceni: Kvíz: souřadnice +nadpis_kvizu: Dopočítejte další bod +--- + +# Výklad + +## Absolutní souřadnice +*Od počátku* + +- Bod 40,25 leží 40 mm vpravo a 25 mm nahoru od počátku 0,0. +- Čárka odděluje X a Y. Desetinná čárka v čísle se v AutoCADu píše tečkou: 10.5 +- Osa X míří vpravo, osa Y nahoru. Tak je natočený model při startu. +- Absolutní bod nezávisí na tom, kde jste skončili minule. + +> Tip: když se výkres „rozjede“ mimo papír, někdo zadal absolutní bod místo posunu. + +## Relativní souřadnice +*Zavináč* + +- `@30,0` znamená 30 mm vpravo od posledního bodu, výška se nemění. +- `@0,40` je 40 mm nahoru. `@-20,0` je 20 mm doleva. +- Zavináč se píše před čísla. Bez něj AutoCAD čte absolutní bod. +- Při kreslení `_LINE` se tak skládá tvar bod za bodem. + +> Tip: obdélník 70 × 40 z bodu 10,10: `@70,0` pak `@0,40` pak `@-70,0` a zavřít volbou Close. + +## Polární souřadnice +*Délka a úhel* + +- `@50<90` je úsečka dlouhá 50 mm směrem 90 stupňů, tedy kolmo nahoru. +- Úhel se měří od kladné osy X proti směru hodinových ručiček. +- `@40<0` míří vpravo, `@40<180` vlevo, `@40<270` dolů. +- Špičatá závorka se píše bez mezery. Mezera by příkaz ukončila. + +> Tip: úhel 45 stupňů je šikmo vpravo nahoru. Délka je vždy skutečná, ne průmět. + +## Zadání: obdélník ze souřadnic +*Cvičení* + +- Spusťte `_LINE` v bodě 20,20. +- Pokračujte `@80,0`, potom `@0,50`, potom `@-80,0`. +- Útvar zavřete volbou Close, ne ručním bodem. +- Změřte `_DIST` mezi levým dolním a pravým horním rohem. Má vyjít 94,3 mm, tedy přepona 80 a 50. + +> Tip: Close spojí poslední bod s prvním. Ruční bod 20,20 nemusí trefit počátek a obrys zůstane otevřený. + + +# Kvíz + +## Co znamená zápis `@0,40`? +- Bod 0,40 od počátku výkresu +- [x] 40 mm nahoru od posledního bodu +- 40 mm vpravo od počátku +- Úhel 40 stupňů +> Zavináč čte posun od posledního bodu. Nula v X a 40 v Y je čistě nahoru. + +## Kam míří `@60<180`? +- 60 mm nahoru +- [x] 60 mm doleva +- 180 mm vpravo +- Do počátku +> Úhel 180 stupňů je záporná osa X, tedy doleva. 60 je délka úsečky. + +## Jak AutoCAD pozná desetinné číslo? +- Čárkou: 10,5,0 +- [x] Tečkou: 10.5 +- Zlomkem 21/2 +- Mezerou 10 5 +> Čárka odděluje X a Y. Desetina se píše tečkou, třeba 10.5,20. + +## Proč útvar zavřít volbou Close? +- Aby se smazala první úsečka +- [x] Aby se poslední bod trefil do prvního +- Close nastaví měřítko +- Close je totéž co Esc +> Ručně opsaný první bod často mine o zlomek milimetru a obrys nejde vyšrafovat. diff --git a/content/technicka-grafika/1-rocnik/05-zakladni-entity.en.md b/content/technicka-grafika/1-rocnik/05-zakladni-entity.en.md new file mode 100644 index 0000000..2ca03d0 --- /dev/null +++ b/content/technicka-grafika/1-rocnik/05-zakladni-entity.en.md @@ -0,0 +1,6 @@ +--- +nazev: Lines, circles, arcs and rectangles +popis: The four commands every part starts with. +nazev_cviceni: Quiz: basic objects +nadpis_kvizu: Pick the right command +--- diff --git a/content/technicka-grafika/1-rocnik/05-zakladni-entity.md b/content/technicka-grafika/1-rocnik/05-zakladni-entity.md new file mode 100644 index 0000000..e41ea6b --- /dev/null +++ b/content/technicka-grafika/1-rocnik/05-zakladni-entity.md @@ -0,0 +1,80 @@ +--- +id: tg-1-zakladni-entity +nazev: Úsečka, kružnice, oblouk a obdélník +popis: Čtyři příkazy, kterými začíná každá součást. +nazev_cviceni: Kvíz: základní entity +nadpis_kvizu: Vyberte správný příkaz +--- + +# Výklad + +## Úsečka a obdélník +*Hrany* + +- `_LINE` chce první a další bod. Končí se Enterem, zavření obrysu volbou Close. +- `_RECTANG` chce dva protilehlé rohy. Z bodu 0,0 a `@90,40` vznikne obdélník 90 × 40. +- Obdélník je jedna uzavřená křivka. Úsečky jsou samostatné objekty. +- Uzavřená křivka se později líp šrafuje a vysouvá do 3D. + +> Tip: když budete tvar vysouvat, kreslete ho jako `_RECTANG` nebo uzavřenou `_PLINE`, ne jako čtyři volné úsečky. + +## Kružnice +*Díry* + +- `_CIRCLE` se nejčastěji zadá středem a poloměrem. +- Volba D přepne na průměr. U díry Ø12 je průměr 12 a poloměr 6. +- Střed díry se kótuje, ne její okraj. Okraj je důsledek průměru. +- Víc stejných děr se nekreslí znovu: první se zkopíruje nebo rozloží do pole. + +> Tip: v zadání Ø znamená průměr. Do příkazu s poloměrem dáte polovinu. + +## Oblouk +*Zaoblení z příkazu* + +- `_ARC` umí oblouk třemi body nebo středem, počátkem a úhlem. +- Zaoblení rohu ale nedělejte obloukem od ruky. Na to je později `_FILLET`. +- Oblouk má směr. Špatné pořadí bodů ho vyklenu na druhou stranu. +- U drážky se oblouk často nahradí `_PLINE` s volbou Arc uprostřed úseček. + +> Tip: tři body oblouku zadávejte v pořadí, v jakém oblouk plyne. Prostřední bod je na oblouku, ne střed kružnice. + +## Zadání: destička s dírou +*Cvičení* + +- `_RECTANG` z 0,0 do `@90,50`. +- Střed destičky je 45,25. Tam `_CIRCLE`, volba D, průměr 16. +- Osu svislou i vodorovnou nechte přesáhnout obrys o 3 mm. Patří na hladinu OSA. +- Uložte jako desticka-90x50.dwg. + +> Tip: polovina z 90 je 45 a polovina z 50 je 25. Střed neměřte od oka. + + +# Kvíz + +## Čím nakreslíte obdélník 90 × 40 jedním objektem? +- Čtyřmi úsečkami `_LINE` +- [x] `_RECTANG` a druhým rohem `@90,40` +- `_CIRCLE` s poloměrem 90 +- `_ARC` o úhlu 90 +> `_RECTANG` chce dva rohy. Relativní `@90,40` je šířka a výška od prvního rohu. + +## Zadání říká díra Ø12. Jaký poloměr má kružnice? +- 12 +- [x] 6 +- 24 +- 3 +> Průměr je 12, poloměr je polovina. V `_CIRCLE` buď poloměr 6, nebo volba D a průměr 12. + +## Kde leží střed destičky 90 × 50, která začíná v 0,0? +- 90,50 +- [x] 45,25 +- 0,0 +- 25,45 +> Střed je v polovině šířky a polovině výšky. + +## Kdy dát přednost uzavřené křivce před čtyřmi úsečkami? +- Nikdy, úsečka je přesnější +- [x] Když se obrys bude šrafovat nebo vysouvat +- Jen u razítka +- Když je tvar otevřený +> Šrafa i vysunutí potřebují uzavřenou oblast. Čtyři úsečky, které se netrefí, oblast neutvoří. diff --git a/content/technicka-grafika/1-rocnik/06-uchopeni-a-orto.en.md b/content/technicka-grafika/1-rocnik/06-uchopeni-a-orto.en.md new file mode 100644 index 0000000..08596b5 --- /dev/null +++ b/content/technicka-grafika/1-rocnik/06-uchopeni-a-orto.en.md @@ -0,0 +1,6 @@ +--- +nazev: Snaps, ortho and polar tracking +popis: How to hit an endpoint and draw square. +nazev_cviceni: Quiz: precise drawing +nadpis_kvizu: Pick the snap and the toggle +--- diff --git a/content/technicka-grafika/1-rocnik/06-uchopeni-a-orto.md b/content/technicka-grafika/1-rocnik/06-uchopeni-a-orto.md new file mode 100644 index 0000000..c9f7f0e --- /dev/null +++ b/content/technicka-grafika/1-rocnik/06-uchopeni-a-orto.md @@ -0,0 +1,80 @@ +--- +id: tg-1-uchopeni +nazev: Úchop, orto a polární trasování +popis: Jak trefit konec čáry a kreslit kolmo. +nazev_cviceni: Kvíz: přesné kreslení +nadpis_kvizu: Vyberte úchop a přepínač +--- + +# Výklad + +## Úchopy +*Koncový bod* + +- Úchop přitáhne kurzor ke geometrii. Bez něj minuta minete konec čáry. +- Zapíná se klávesou F3 nebo v stavovém řádku. Nastavení je `_OSNAP`. +- Na začátek stačí koncový bod, střed a střed kružnice. +- Kolmice a tečna se hodí, až budete napojovat čáru na kružnici. + +> Tip: úchop Střed u úsečky je její polovina. Střed u kružnice je její osa. Jsou to dva různé úchopy. + +## Orto a polární úhel +*F8 a F10* + +- Orto, klávesa F8, pustí kurzor jen vodorovně a svisle. +- Polární trasování F10 drží i šikmé úhly, obvykle po 45 nebo po 15 stupních. +- Orto a volné šikmé kreslení nejdou dohromady. Na šikmou čáru orto vypněte. +- Délku při zapnutém ortu napište jen číslem a Enter. Směr určuje myš. + +> Tip: vodorovná čára dlouhá 40 mm je F8, kurzor doprava, napsat 40 a Enter. Nemusíte počítat druhý bod. + +## Proč se výkres rozpadne +*Minutí* + +- Dvě čáry, které se „téměř“ dotýkají, nejdou oříznout ani vyšrafovat. +- Mezera vznikne, když kliknete vedle koncového bodu a úchop je vypnutý. +- `_ZOOM` na roh ukáže mezeru, která na celém výkresu není vidět. +- Opravený roh se napojí úchopem, ne posunem od oka. + +> Tip: než odevzdáte výkres, přibližte každý roh. Mezera menší než milimetr na tisku vynikne. + +## Zadání: kříž přesně ve středu +*Cvičení* + +- Nakreslete obdélník 80 × 60 z bodu 0,0. +- Svislou osu veďte z úchopu Střed dolní hrany do úchopu Střed horní hrany. +- Vodorovnou osu veďte ze Středu levé hrany do Středu pravé. +- Osy se mají protnout v bodě 40,30. Ověřte to `_ID`. + +> Tip: `_ID` vypíše souřadnice úchopu. Když průsečík není 40,30, jedna osa minula střed hrany. + + +# Kvíz + +## Která klávesa zapíná úchopy? +- F8 +- [x] F3 +- F10 +- Esc +> F3 jsou úchopy, F8 je orto, F10 je polární trasování. + +## Co dělá orto? +- Chytá konce čar +- [x] Drží kreslení ve vodorovném a svislém směru +- Nastaví milimetry +- Maže poslední úsečku +> Se zapnutým ortem kurzor neuhne do šikma. Délku pak stačí napsat číslem. + +## Proč dvě čáry, které se na obrazovce dotýkají, nejdou vyšrafovat? +- Šrafa vyžaduje kóty +- [x] Mezi nimi je mezera, konec minul úchop +- Hladina OSA to zakazuje +- Orto je zapnuté +> Od oka minutý konec nechá škvíru. Přiblížení a úchop Koncový bod ji ukážou. + +## Který úchop najde polovinu úsečky? +- Střed kružnice +- [x] Střed +- Koncový bod +- Kolmice +> Úchop Střed na úsečce je její polovina. Na kružnici je to jiný úchop, střed kružnice. diff --git a/content/technicka-grafika/1-rocnik/07-posun-kopie-otoceni.en.md b/content/technicka-grafika/1-rocnik/07-posun-kopie-otoceni.en.md new file mode 100644 index 0000000..0409703 --- /dev/null +++ b/content/technicka-grafika/1-rocnik/07-posun-kopie-otoceni.en.md @@ -0,0 +1,6 @@ +--- +nazev: Move, copy, rotate and mirror +popis: Change a finished shape instead of drawing it again. +nazev_cviceni: Quiz: editing +nadpis_kvizu: Match the command to the task +--- diff --git a/content/technicka-grafika/1-rocnik/07-posun-kopie-otoceni.md b/content/technicka-grafika/1-rocnik/07-posun-kopie-otoceni.md new file mode 100644 index 0000000..611ca1a --- /dev/null +++ b/content/technicka-grafika/1-rocnik/07-posun-kopie-otoceni.md @@ -0,0 +1,80 @@ +--- +id: tg-1-posun-kopie +nazev: Posun, kopie, otočení a zrcadlení +popis: Úprava hotového tvaru místo kreslení znovu. +nazev_cviceni: Kvíz: úpravy +nadpis_kvizu: Vyberte příkaz podle zadání +--- + +# Výklad + +## Posun a kopie +*Z místa na místo* + +- `_MOVE` objekt odnese. Na původním místě nic nezůstane. +- `_COPY` nechá vzor a přidá další. Ptá se na základní bod a pak na nové body. +- Základní bod vyberte úchopem, třeba roh nebo střed. Od oka se kopie rozjede. +- Víc kopií za sebou vznikne opakovaným zadáním dalšího bodu, bez nového příkazu. + +> Tip: posun o 30 mm doprava je základní bod kdekoli, druhý bod `@30,0`. + +## Otočení +*Kolem bodu* + +- `_ROTATE` chce základní bod a úhel. Kladný úhel je proti směru hodin. +- 90 otočí doleva, -90 nebo 270 doprava. +- Volba Reference vezme současný úhel hrany a řekne, jaký má být nový. +- Otáčí se kolem vybraného bodu, ne kolem středu obrazovky. + +> Tip: díru v rohu otočíte kolem středu destičky, ne kolem středu díry, jinak odjede z rozteče. + +## Zrcadlení +*Souměrnost* + +- `_MIRROR` chce dva body osy zrcadlení. Osa nemusí být nakreslená předem. +- Svislá osa jsou dva body se stejným X. Vodorovná dva body se stejným Y. +- Na otázku, zda smazat zdroj, dejte Ne, když má zůstat obě poloviny. +- Text se při zrcadlení může otočit naruby. Proměnná MIRRTEXT s hodnotou 0 nechá text čitelný. + +> Tip: souměrnou součást nakreslete z poloviny a zrcadlete. Druhou polovinu neobkreslujte. + +## Zadání: druhá díra zrcadlem +*Cvičení* + +- Destička 100 × 40, levý dolní roh v 0,0. +- Díru Ø10 dejte středem do bodu 20,20. +- Zrcadlete ji přes svislou osu destičky. Osa prochází body 50,0 a 50,40. +- Zdroj nemažte. Druhá díra má střed v 80,20. + +> Tip: 100 minus 20 je 80. Zrcadlo kolem x = 50 převede 20 na 80 a výšku 20 nechá. + + +# Kvíz + +## Čím odnesete objekt, aby na původním místě nezůstal? +- `_COPY` +- [x] `_MOVE` +- `_MIRROR` +- `_ROTATE` +> Kopie zdroj nechá. Posun ho přestěhuje. + +## Jaký úhel otočí tvar o 90 stupňů ve směru hodin? +- 90 +- [x] -90 +- 180 +- 45 +> Kladný úhel jde proti hodinám. Po směru hodin je záporný, tedy -90. + +## Přes co se zrcadlí díra se středem 20,20 na destičce široké 100? +- Přes bod 0,0 +- [x] Přes svislou osu x = 50 +- Přes vodorovnou osu y = 20 +- Přes hranu x = 100 +> Osa souměrnosti destičky je uprostřed šířky. 20 a 80 jsou od ní stejně daleko. + +## Co má být základní bod posunu? +- Libovolné místo vedle objektu, od oka +- [x] Úchop, třeba roh nebo střed +- Vždy počátek 0,0 +- Střed obrazovky +> Úchop podrží rozměr. Kliknutí vedle rohu posune tvar o neznámý kousek navíc. diff --git a/content/technicka-grafika/1-rocnik/08-zadani-desticka.en.md b/content/technicka-grafika/1-rocnik/08-zadani-desticka.en.md new file mode 100644 index 0000000..1920b3e --- /dev/null +++ b/content/technicka-grafika/1-rocnik/08-zadani-desticka.en.md @@ -0,0 +1,6 @@ +--- +nazev: Assignment: a plate +popis: The whole first-year drawing on one part. +nazev_cviceni: Quiz: the plate +nadpis_kvizu: Check the finished drawing +--- diff --git a/content/technicka-grafika/1-rocnik/08-zadani-desticka.md b/content/technicka-grafika/1-rocnik/08-zadani-desticka.md new file mode 100644 index 0000000..08464d1 --- /dev/null +++ b/content/technicka-grafika/1-rocnik/08-zadani-desticka.md @@ -0,0 +1,80 @@ +--- +id: tg-1-zadani-desticka +nazev: Zadání: destička +popis: Celý výkres z prvního ročníku na jedné součásti. +nazev_cviceni: Kvíz: destička +nadpis_kvizu: Zkontrolujte hotový výkres +--- + +# Výklad + +## Co se odevzdává +*Zadání* + +- Destička 120 × 70 mm, tloušťka se nekreslí, jde o jeden pohled. +- Čtyři díry Ø8. Středy jsou 15 mm od levého i pravého kraje a 15 mm od dolního i horního kraje. +- Uprostřed je podélná drážka 40 × 16 se zaoblenými konci. Zatím stačí obdélník, zaoblení přijde ve druháku. +- Výkres je na A4, měřítko 1:1, rámeček a razítko z první lekce. + +> Tip: středy děr jsou body 15,15, pak 105,15, pak 15,55 a 105,55. 120 minus 15 je 105, 70 minus 15 je 55. + +## Hladiny a čáry +*Pořádek* + +- OBRYS je tlustá plná čára. OSA je tenká čerchovaná. KOTA zatím nechte prázdnou. +- Každá díra má svislou i vodorovnou osu, která přesahuje obrys o 3 mm. +- Destička má jednu vodorovnou a jednu svislou osu souměrnosti přes celý tvar. +- Skryté hrany tu nejsou. Pohled je zepředu a díry jsou průchozí, jejich okraj je vidět. + +> Tip: průchozí díra v tomto pohledu není čárkovaná. Čárkovaná by byla hrana schovaná za materiálem. + +## Postup +*Po řadě* + +- Nejdřív rámeček a razítko, potom obrys `_RECTANG` 120 × 70. +- Jednu díru `_CIRCLE` o průměru 8 a další tři `_COPY` nebo dvě `_MIRROR`. +- Drážku `_RECTANG` 40 × 16 se středem ve středu destičky. Její levý dolní roh je 40,27. +- Osy dokreslete úchopem Střed. `_DIST` mezi protilehlými dírami ve vodorovném směru má být 90. + +> Tip: střed destičky je 60,35. Drážka sahá 20 mm doleva a doprava a 8 mm nahoru a dolů. + +## Kontrola před odevzdáním +*Co musí sedět* + +- V razítku je název Destička, měřítko 1:1 a materiál, třeba ocel. +- Žádná čára obrysu neleží na hladině OSA a žádná osa není plná. +- Přiblížené rohy nemají mezeru. Kružnice se dotýká svých os ve středu. +- Soubor se jmenuje desticka-120x70.dwg a je v milimetrech. + +> Tip: vypněte hladinu OBRYS. Zůstat mají jen osy a rámeček. Zapněte ji zpět a totéž udělejte s OSA. + + +# Kvíz + +## Kde leží střed pravé horní díry? +- 15,15 +- [x] 105,55 +- 120,70 +- 60,35 +> Od pravého kraje 15 mm je x = 105. Od horního kraje 15 mm je y = 55. + +## Jaká je vodorovná rozteč levé a pravé díry? +- 120 +- 15 +- [x] 90 +- 105 +> 105 minus 15 je 90. Tolik má ukázat `_DIST` mezi středy. + +## Kam patří obrys destičky? +- Na hladinu OSA +- [x] Na hladinu OBRYS, tlustou plnou čarou +- Čárkovaně, protože díry jsou průchozí +- Do razítka +> Viditelný obrys je tlustá plná čára. Osa je samostatná hladina. + +## Proč se průchozí díra v tomto pohledu nekreslí čárkovaně? +- Čárkovaná čára je zakázaná +- [x] Její okraj je z pohledu vidět +- Díra se kreslí jen osou +- Měřítko je 1:2 +> Čárkovaně se kreslí hrana, kterou materiál zakrývá. Okraj díry v čelním pohledu vidíme. diff --git a/content/technicka-grafika/2-rocnik/01-orez-a-ekvidistanta.en.md b/content/technicka-grafika/2-rocnik/01-orez-a-ekvidistanta.en.md new file mode 100644 index 0000000..0b5fb23 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/01-orez-a-ekvidistanta.en.md @@ -0,0 +1,6 @@ +--- +nazev: Trim, extend and offset +popis: Fix lines that overshoot, and keep a constant gap. +nazev_cviceni: Quiz: trim and offset +nadpis_kvizu: Pick the command and the distance +--- diff --git a/content/technicka-grafika/2-rocnik/01-orez-a-ekvidistanta.md b/content/technicka-grafika/2-rocnik/01-orez-a-ekvidistanta.md new file mode 100644 index 0000000..d132ae3 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/01-orez-a-ekvidistanta.md @@ -0,0 +1,80 @@ +--- +id: tg-2-orez-ekvidistanta +nazev: Ořez, prodloužení a ekvidistanta +popis: Úprava čar, které přesahují nebo mají stálý odstup. +nazev_cviceni: Kvíz: ořez a odstup +nadpis_kvizu: Vyberte příkaz a vzdálenost +--- + +# Výklad + +## Ořez a prodloužení +*Na hranu* + +- `_TRIM` uřízne kus objektu po hraně, kterou vyberete jako řeznou. +- V novějším AutoCADu stačí Enter bez výběru hran a řeže se o všechno, co čára protíná. +- `_EXTEND` dělá opak: natáhne čáru až k vybrané hraně. +- Maže se ta část, na kterou kliknete. Klik na špatnou stranu uřízne to, co mělo zůstat. + +> Tip: když ořez „nebere“, čáry se ve skutečnosti neprotínají. Přibližte roh a napojte ho úchopem. + +## Ekvidistanta +*Stálý odstup* + +- `_OFFSET` vytvoří rovnoběžnou křivku v zadané vzdálenosti. +- Hodí se na tloušťku stěny, rámeček i osazení. Zadáte vzdálenost a stranu. +- U kružnice vznikne soustředná kružnice. Průměr se změní o dvojnásobek odstupu. +- Ekvidistanta obdélníku udělá menší nebo větší obdélník se stejnými rohy, ne se zaoblením. + +> Tip: stěna tlustá 8 mm je ekvidistanta obrysu o 8 mm dovnitř. U kružnice Ø40 tak vznikne Ø24. + +## Co neřeže +*Otevřený roh* + +- Ořez potřebuje průsečík. Dvě čáry, které se jen míjejí, se neoříznou. +- Nejdřív je prodlužte nebo je nakreslete znovu s úchopem, teprve pak řežte. +- Ekvidistanta otevřené křivky zůstane otevřená. Rohy se samy nedovřou. +- Po ořezu zkontrolujte, že nezůstaly pahýly kratší než milimetr. Ty `_ERASE` smaže. + +> Tip: pahýl po ořezu je pořád objekt. Na tisku udělá tečku, která do výkresu nepatří. + +## Zadání: rámeček stěny +*Cvičení* + +- Vnější obdélník 100 × 60. Ekvidistantou o 8 mm dovnitř udělejte vnitřní obrys. +- Vnitřní obdélník má tedy 84 × 44. +- Čáry, které po dalších úpravách přesáhnou vnější roh, ořízněte. +- Oba obrysy nechte na hladině OBRYS. Kóty zatím nepřidávejte. + +> Tip: 100 minus dvakrát 8 je 84. 60 minus 16 je 44. Když vnitřek vyjde jinak, odstup šel ven, ne dovnitř. + + +# Kvíz + +## Který příkaz uřízne přesahující kus čáry? +- `_OFFSET` +- [x] `_TRIM` +- `_EXTEND` +- `_MOVE` +> Ořez bere kus za řeznou hranou. Prodloužení čáru naopak natahuje. + +## Ekvidistanta kružnice Ø40 o 8 mm dovnitř má průměr? +- 32 +- [x] 24 +- 48 +- 8 +> Poloměr klesne o 8, průměr o 16. 40 minus 16 je 24. + +## Proč ořez na dvou čarách nic neudělá? +- Orto je zapnuté +- [x] Čáry se neprotínají +- Chybí razítko +- Objekt je kružnice +> Bez průsečíku není kde řezat. Nejdřív čáry napojte, potom řežte. + +## Vnitřní obrys obdélníku 100 × 60 po ekvidistantě 8 mm dovnitř je? +- 100 × 60 +- 92 × 52 +- [x] 84 × 44 +- 108 × 68 +> Z šířky i výšky se ubere 8 mm z každé strany, tedy 16 mm. 84 × 44. diff --git a/content/technicka-grafika/2-rocnik/02-zaobleni-a-zkoseni.en.md b/content/technicka-grafika/2-rocnik/02-zaobleni-a-zkoseni.en.md new file mode 100644 index 0000000..30afc38 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/02-zaobleni-a-zkoseni.en.md @@ -0,0 +1,6 @@ +--- +nazev: Fillets and chamfers +popis: A corner by a radius or by two distances. +nazev_cviceni: Quiz: corners +nadpis_kvizu: Work out the corner +--- diff --git a/content/technicka-grafika/2-rocnik/02-zaobleni-a-zkoseni.md b/content/technicka-grafika/2-rocnik/02-zaobleni-a-zkoseni.md new file mode 100644 index 0000000..17e40f1 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/02-zaobleni-a-zkoseni.md @@ -0,0 +1,80 @@ +--- +id: tg-2-zaobleni-zkoseni +nazev: Zaoblení a zkosení +popis: Roh poloměrem nebo dvěma vzdálenostmi. +nazev_cviceni: Kvíz: rohy +nadpis_kvizu: Dopočítejte roh +--- + +# Výklad + +## Zaoblení +*Poloměr* + +- `_FILLET` spojí dvě úsečky obloukem. Volba R nastaví poloměr. +- Poloměr 8 znamená oblouk o poloměru 8 mm, ne průměr. +- R0 ostrý roh jen napojí, oblouk nevznikne. Hodí se na nedotažené čáry. +- Příkaz si poloměr pamatuje. Než zaoblíte jiný roh, R zkontrolujte. + +> Tip: u drážky se zaobleným koncem je poloměr polovina šířky drážky. Drážka 16 mm má R8. + +## Zkosení +*Dvě vzdálenosti* + +- `_CHAMFER` srazí roh úsečkou. Volba D zadá dvě vzdálenosti od vrcholu. +- Stejné vzdálenosti 2 a 2 srazí roh pod 45 stupňů, pokud jsou hrany kolmé. +- Různé vzdálenosti, třeba 2 a 4, dají šikminu, která není 45 stupňů. +- Zkosení se kótuje délkou a úhlem, nebo oběma délkami. Ne poloměrem. + +> Tip: na výkresu je zkosení 2 × 45°. V příkazu jsou to vzdálenosti 2 a 2, ne úhel napsaný místo D. + +## Kdy který roh +*Dílec* + +- Zaoblení bývá tam, kde se obrábí frézou nebo kde ostrá hrana vadí. +- Zkosení bývá na hraně díry a na konci hřídele, aby díl navedl protikus. +- Na jednom rohu nedávejte zaoblení i zkosení. Zadání říká jen jedno. +- Po zaoblení se změní délka rovné hrany. Kóta rovné části už není celá šířka. + +> Tip: hrana dlouhá 40 mm se dvěma zaobleními R5 na koncích má rovný úsek 30 mm. + +## Zadání: drážka a sražení +*Cvičení* + +- Obdélník 80 × 30. Na obou kratších koncích `_FILLET` s R8, na obou dlouhých hranách. +- Vznikne oválná drážka: rovný střed je dlouhý 64 mm, šířka zůstane 30? Pozor, R8 na výšce 30 nejde, šířka drážky musí být 16. +- Překreslete tvar jako drážku 64 × 16 a konce zaoblete R8. +- Jeden vnější roh destičky 80 × 50 mimo drážku srazte `_CHAMFER` 2 a 2. + +> Tip: poloměr zaoblení konce drážky se rovná polovině její šířky. 16 / 2 = 8. + + +# Kvíz + +## Co nastaví `_FILLET` volbou R8? +- Zkosení 8 × 8 +- [x] Oblouk o poloměru 8 mm +- Průměr 8 +- Osazení o 8 mm +> R je poloměr oblouku. Průměr oblouku by byl 16. + +## Jak v příkazu zadáte zkosení 2 × 45° na kolmém rohu? +- R2 +- [x] Vzdálenosti 2 a 2 +- Úhel 45 místo obou vzdáleností, bez délek +- Ekvidistantu 2 +> Stejné vzdálenosti na kolmých hranách dají 45 stupňů. Do příkazu patří délky. + +## Jaký poloměr má konec drážky široké 16 mm? +- 16 +- [x] 8 +- 4 +- 32 +> Konec je půlkruh. Jeho poloměr je polovina šířky. + +## Co udělá zaoblení R0? +- Smaže roh +- [x] Napojí čáry ostrým rohem +- Udělá kružnici +- Nastaví měřítko +> Nulový poloměr je způsob, jak dvě minuté čáry spojit do ostrého rohu. diff --git a/content/technicka-grafika/2-rocnik/03-hladiny.en.md b/content/technicka-grafika/2-rocnik/03-hladiny.en.md new file mode 100644 index 0000000..ecdd7b2 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/03-hladiny.en.md @@ -0,0 +1,6 @@ +--- +nazev: Layers +popis: Colour, linetype and weight in one place. +nazev_cviceni: Quiz: layers +nadpis_kvizu: Decide which layer an object belongs on +--- diff --git a/content/technicka-grafika/2-rocnik/03-hladiny.md b/content/technicka-grafika/2-rocnik/03-hladiny.md new file mode 100644 index 0000000..6a93333 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/03-hladiny.md @@ -0,0 +1,80 @@ +--- +id: tg-2-hladiny +nazev: Hladiny +popis: Barva, typ čáry a tloušťka na jednom místě. +nazev_cviceni: Kvíz: hladiny +nadpis_kvizu: Rozhodněte, co patří na kterou hladinu +--- + +# Výklad + +## Proč hladiny +*Pořádek* + +- Hladina nese barvu, typ čáry i tloušťku. Objekt, který na ní vznikne, je zdědí. +- Obrys, osa, skrytá hrana, šrafa, kóta a rámeček jsou samostatné hladiny. +- Vypnutá hladina na obrazovce zmizí, ale tisknout se pořád může. Zmrazená se netiskne a nepřepočítává. +- Zamčená hladina je vidět, ale nejde omylem posunout. + +> Tip: kóty nikdy nekreslete na OBRYS. Až budete obrys měnit, kóty by odjely s ním. + +## Aktuální hladina +*Než kliknete* + +- Nový objekt padne na aktuální hladinu. Je to ta, která je ve správci vybraná jako současná. +- `_LAYER` založí hladinu. Poklepáním ji nastavíte jako aktuální. +- Objekt na špatné hladině přesuňte ve vlastnostech, příkaz `_PROPERTIES` nebo horní lišta. +- Barvu a typ čáry nechte ByLayer. Barva nastavená na objektu se se hladinou přestane řídit. + +> Tip: ByLayer znamená „ber to, co říká hladina“. Je to výchozí a na školním výkresu má tak zůstat. + +## Názvy, které obstojí +*Úmluva* + +- OBRYS, OSA, SKRYTE, SRAFA, KOTA, RAM, TEXT. Krátce a bez mezer. +- OBRYS je bílá nebo černá, tloušťka 0,50. Ostatní tenké, 0,25, každá svou barvou. +- SKRYTE má typ DASHED, OSA typ CENTER. Ostatní Continuous. +- Hladinu 0 nemažte a nekreslete na ni součást. Je systémová. + +> Tip: hladina 0 je schovaná v blocích. Součást na ní se později špatně barví. + +## Zadání: šest hladin +*Cvičení* + +- V souboru destičky založte OBRYS, OSA, SKRYTE, SRAFA, KOTA a RAM. +- Obrys přesuňte na OBRYS, osy na OSA, rámeček na RAM. +- OSA musí mít typ CENTER a po `_LTSCALE` 0,5 nebo 1 musí být čerchování vidět. +- Vypněte OBRYS. Na obrazovce smí zůstat osy a rámeček, nic z tvaru. + +> Tip: když osa vypadá plně, typ čáry není načtený, nebo je LTSCALE mimo rozumné měřítko. + + +# Kvíz + +## Kam patří kóta? +- Na hladinu OBRYS +- [x] Na vlastní hladinu KOTA +- Na hladinu 0 +- Kamkoli, hladina je jen barva pozadí +> Kóta má vlastní tenkou čáru a při úpravě obrysu nesmí odjet s ním. + +## Co znamená ByLayer? +- Objekt barvu hladiny ignoruje +- [x] Objekt si bere barvu a typ čáry z hladiny +- Hladina je zamčená +- Objekt se netiskne +> ByLayer je správné nastavení. Barva „natvrdo“ na objektu se hladinou už neřídí. + +## Čím se liší vypnutá a zmrazená hladina? +- Ničím +- [x] Zmrazená se navíc netiskne a výkres ji nepočítá +- Vypnutá se nedá znovu zapnout +- Zmrazená je vidět červeně +> Vypnutí je jen obrazovka. Zmrazení hladinu vyřadí i z tisku a z přepočtu. + +## Proč nekreslit součást na hladinu 0? +- Hladina 0 nejde použít +- [x] Je systémová a v blocích se chová jinak +- Nepouští tlusté čáry +- Smí na ni jen razítko +> Hladina 0 má zvláštní roli uvnitř bloků. Součást patří na pojmenované hladiny. diff --git a/content/technicka-grafika/2-rocnik/04-srafy.en.md b/content/technicka-grafika/2-rocnik/04-srafy.en.md new file mode 100644 index 0000000..7619115 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/04-srafy.en.md @@ -0,0 +1,6 @@ +--- +nazev: Hatches +popis: Sectioned material, hatch scale and a closed boundary. +nazev_cviceni: Quiz: hatches +nadpis_kvizu: Decide what gets hatched +--- diff --git a/content/technicka-grafika/2-rocnik/04-srafy.md b/content/technicka-grafika/2-rocnik/04-srafy.md new file mode 100644 index 0000000..300909f --- /dev/null +++ b/content/technicka-grafika/2-rocnik/04-srafy.md @@ -0,0 +1,80 @@ +--- +id: tg-2-srafy +nazev: Šrafy +popis: Řez materiálem, měřítko šrafy a uzavřená oblast. +nazev_cviceni: Kvíz: šrafy +nadpis_kvizu: Rozhodněte, co se šrafuje +--- + +# Výklad + +## Co šrafa říká +*Materiál* + +- Šrafa v řezu znamená materiál, který řezná rovina proťala. +- Ocel se na školním výkresu šrafuje tenkými čarami pod 45 stupňů. +- Dva sousední díly mají šrafu na opačnou stranu, nebo jinou rozteč. +- Díra, drážka a prázdno se nešrafují. Není tam materiál. + +> Tip: šrafa pod 45 stupni nesmí být rovnoběžná s obrysem. Když je obrys šikmý, úhel šrafy změňte. + +## Příkaz +*Uzavřená oblast* + +- `_HATCH` a vzor ANSI31. Klikněte dovnitř uzavřené oblasti. +- Oblast musí být dovřená. Mezera v rohu šrafu zastaví nebo ji vylije ven. +- Měřítko vzoru zvětší rozteč čar. Moc hustá šrafa na tisku zčerná. +- Šrafa patří na hladinu SRAFA, tenkou čarou. Ne na OBRYS. + +> Tip: asociativní šrafa se přizpůsobí, když obrys později natáhnete. Na školní práci ji nechte zapnutou. + +## Co se v podélném řezu nešrafuje +*Výjimky* + +- Hřídel, žebro, šroub a pero v podélném řezu zůstávají bez šrafy. +- V příčném řezu se tatáž hřídel šrafuje. Rozhoduje směr řezu, ne součást. +- Tenká stěna řezaná podélně se šrafuje. Žebro ne, aby nepůsobilo jako plný kus. +- Obrys šrafované plochy je pořád tlustá čára. Šrafa ho nenahrazuje. + +> Tip: podélný řez hřídele ukáže osazení obrysem. Šrafa by z hřídele udělala zdánlivě plný hranol. + +## Zadání: řez destičkou +*Cvičení* + +- Destičku 80 × 20 v řezu A-A vyšrafujte ANSI31, měřítko tak, aby rozteč byla zhruba 2 až 3 mm. +- Díru Ø10 uprostřed nešrafujte. Šrafa končí na její hraně. +- Šrafu dejte na hladinu SRAFA. Vypnutím této hladiny musí zmizet jen šrafa, obrys zůstane. +- Když šrafa vyteče ven, najděte mezeru v obrysu a roh napojte. + +> Tip: jedna oblast vlevo od díry a jedna vpravo. Obě mohou být jedna šrafa, pokud příkaz umí víc oblastí. + + +# Kvíz + +## Co znamená šrafa v řezu? +- Skrytou hranu +- [x] Materiál proťatý řeznou rovinou +- Závit +- Měřítko 1:2 +> Šrafuje se jen to, co řez skutečně prochází. Díra je prázdná. + +## Jaký vzor se na škole používá pro ocel? +- Plná výplň +- [x] ANSI31, tenké čáry pod 45 stupňů +- Tečkování +- Čerchovaná osa +> ANSI31 je běžný strojařský vzor. Úhel změňte, jen když by splynul s obrysem. + +## Proč šrafa vyteče mimo součást? +- Je zapnuté orto +- [x] Obrys není uzavřený +- Chybí kóta +- Hladina je ByLayer +> I malá mezera v rohu oblast otevře. Napojte roh a šrafu opakujte. + +## Šrafuje se hřídel v podélném řezu? +- Ano, vždy +- [x] Ne +- Jen když má drážku +- Jen ve 3D +> Hřídel, žebro a šroub se v podélném řezu nechávají prázdné. V příčném řezu se hřídel šrafuje. diff --git a/content/technicka-grafika/2-rocnik/05-koty-linearni.en.md b/content/technicka-grafika/2-rocnik/05-koty-linearni.en.md new file mode 100644 index 0000000..46918a3 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/05-koty-linearni.en.md @@ -0,0 +1,6 @@ +--- +nazev: Linear dimensions +popis: The real size, extension lines and a dimension style. +nazev_cviceni: Quiz: linear dimensions +nadpis_kvizu: Read the dimension correctly +--- diff --git a/content/technicka-grafika/2-rocnik/05-koty-linearni.md b/content/technicka-grafika/2-rocnik/05-koty-linearni.md new file mode 100644 index 0000000..20556d0 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/05-koty-linearni.md @@ -0,0 +1,80 @@ +--- +id: tg-2-koty-linearni +nazev: Lineární kóty +popis: Skutečný rozměr, pomocné čáry a styl kót. +nazev_cviceni: Kvíz: lineární kóty +nadpis_kvizu: Přečtěte kótu správně +--- + +# Výklad + +## Co kóta říká +*Skutečný rozměr* + +- Kóta je skutečný rozměr součásti, ne délka čáry na papíře. +- Na výkresu 1:2 je čára poloviční a číslo zůstává celé. +- `_DIMLINEAR` chce počátek první pomocné čáry, druhé a polohu kótovací čáry. +- Body chytejte úchopem. Kóta z bodu vedle rohu je o milimetr špatně a tváří se přesně. + +> Tip: číslo na kótě nepřepisujte ručně. Když nesedí, je špatně geometrie, ne text. + +## Vzhled +*Čitelné číslo* + +- Pomocné čáry odstupují od obrysu asi o 1 mm a přesahují kótovací čáru. +- Sousední kóty mají stejný odstup, třeba 10 mm od obrysu a 7 mm mezi sebou. +- Číslo se píše nad kótovací čáru, ne přes obrys. Kóta neprotíná šrafu, když to jde jinak. +- `_DIMSTYLE` nastaví šipky, písmo a jednotky jednou pro celý výkres. + +> Tip: výška písma na A4 bývá 3,5 mm. Menší text se na tisku nedá číst. + +## Řetězec a základna +*Víc rozměrů* + +- Řetězec klade kóty za sebou. Součet dílčích kót má dát celkový rozměr. +- Kóta od základny měří všechno od jedné hrany. Hodí se na rozteče od dorazu. +- Celkový rozměr a součet dílčích se na jedné hraně neopakují bez důvodu. +- Kóta patří tam, odkud se rozměr bude vyrábět, ne doprostřed šrafy. + +> Tip: 20 + 45 + 15 = 80. Když součet nesedí s celkovou kótou, jedna pomocná čára minula roh. + +## Zadání: destička 120 × 70 +*Cvičení* + +- Okótujte šířku 120 a výšku 70. +- Vodorovně přidejte 15 od levého kraje ke středu díry a 90 mezi středy děr. +- Svisle 15 od dolní hrany ke středu díry. +- Všechny kóty na hladinu KOTA. `_DIST` mezi rohy musí ukázat stejná čísla jako kóty. + +> Tip: kótujte středy děr, ne jejich okraje. Okraj určí až kóta průměru. + + +# Kvíz + +## Co se píše do lineární kóty na výkresu 1:2? +- Polovina skutečného rozměru +- [x] Skutečný rozměr +- Dvojnásobek +- Měřítko 1:2 místo čísla +> Měřítko mění délku čáry na papíře. Číslo kóty je rozměr součásti. + +## Proč nepřepisovat text kóty ručně? +- AutoCAD to zakazuje vždy +- [x] Špatné číslo skrývá chybu geometrie +- Text musí být v palcích +- Kóta by zmizela z hladiny +> Správná kóta měří geometrii. Ruční text 120 na čáře dlouhé 119 je lež. + +## Od čeho se kótuje poloha díry? +- Od okraje kružnice +- [x] Od středu +- Od osy razítka +- Od nejbližší šrafy +> Výroba potřebuje střed. Průměr díry je samostatná kóta. + +## Co má platit pro řetězec 20, 45 a 15 na součásti dlouhé 80? +- Součet má být 100 +- [x] Součet má být 80 +- Celková kóta se vynechá vždy +- Řetězec se kreslí čerchovaně +> 20 + 45 + 15 = 80. Nesoulad znamená minutý úchop na některé pomocné čáře. diff --git a/content/technicka-grafika/2-rocnik/06-koty-prumeru-a-uhlu.en.md b/content/technicka-grafika/2-rocnik/06-koty-prumeru-a-uhlu.en.md new file mode 100644 index 0000000..9b60f02 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/06-koty-prumeru-a-uhlu.en.md @@ -0,0 +1,6 @@ +--- +nazev: Diameter, radius and angle dimensions +popis: The diameter symbol, a fillet radius and a chamfer angle. +nazev_cviceni: Quiz: diameter and angle +nadpis_kvizu: Match the symbol to the size +--- diff --git a/content/technicka-grafika/2-rocnik/06-koty-prumeru-a-uhlu.md b/content/technicka-grafika/2-rocnik/06-koty-prumeru-a-uhlu.md new file mode 100644 index 0000000..17ce4d0 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/06-koty-prumeru-a-uhlu.md @@ -0,0 +1,80 @@ +--- +id: tg-2-koty-prumeru +nazev: Kóty průměru, poloměru a úhlu +popis: Značka průměru, poloměr zaoblení a úhel zkosení. +nazev_cviceni: Kvíz: průměr a úhel +nadpis_kvizu: Doplňte značku ke správnému rozměru +--- + +# Výklad + +## Průměr a poloměr +*Díra a oblouk* + +- `_DIMDIAMETER` kótuje kružnici. Před číslo patří značka průměru. +- `_DIMRADIUS` kótuje oblouk. Před číslo patří R. +- Díra má kótu průměru, i když jste ji kreslili poloměrem. Výroba vrtá průměrem. +- Zaoblení rohu má kótu poloměru. Jedno R na výkresu platí pro všechny stejné rohy, když to poznámka říká. + +> Tip: díru Ø8 nekótujte jako R4. Nástroj se jmenuje podle průměru. + +## Úhel +*Zkosení a klín* + +- `_DIMANGULAR` měří úhel mezi dvěma úsečkami nebo obloukem. +- Zkosení 2 × 45° se často zapíše poznámkou, ne obloukovou kótou v každém rohu. +- Úhel se měří ve stupních. 45 stupňů je polovina pravého úhlu. +- Kóta úhlu nesmí překrýt číslo sousední délky. Odsuňte ji dál od vrcholu. + +> Tip: pravý úhel se na strojařském výkresu obvykle nekótuje. 90 stupňů se předpokládá. + +## Kolik desetin +*Přesnost* + +- Školní kóta celého milimetru nemá zbytečné desetinné nuly, pokud styl neříká jinak. +- Průměr a poloměr zaokrouhlete stejně jako délky. Nemíchejte 8 a 8,00 bez důvodu. +- `_DIMSTYLE` nastaví přesnost i značky najednou. Neupravujte každou kótu ručně. +- Kóta, která ukazuje 7,99 místo 8, minula střed. Geometrii opravte, text ne. + +> Tip: 7,99 je skoro jistě úchop vedle středu. `_ID` středu díry to ukáže. + +## Zadání: díra a roh +*Cvičení* + +- Na destičce okótujte díru Ø8 příkazem `_DIMDIAMETER`. +- Zaoblení R8 na drážce okótujte `_DIMRADIUS` jednou. K ostatním stejné poznámkou. +- Zkosení 2 × 45° zapište k příslušné hraně. +- Žádná kóta průměru nesmí ukazovat 4 ani R8 u díry, která se vrtá. + +> Tip: čtyři stejné díry se kótují jednou, s poznámkou 4× Ø8, plus rozteč středů. + + +# Kvíz + +## Jak se kótuje vrtaná díra o průměru 8 mm? +- R8 +- R4 +- [x] Ø8 +- 8 bez značky, jako délka +> Vrták se volí podle průměru. Poloměr by výrobce přepočítával a snadno spletl. + +## Co znamená R8? +- Průměr 8 +- [x] Poloměr zaoblení 8 mm +- Rozteč 8 mm +- Tloušťka čáry 8 +> R je poloměr oblouku. U díry se R nepoužívá. + +## Kóta díry ukazuje 7,99. Co s tím? +- Přepsat text na 8 +- [x] Opravit geometrii, střed minul úchop +- Smazat všechny kóty +- Změnit měřítko na 1:2 +> Text by lež zakryl. Střed díry má ležet přesně tam, kde ho kóty polohy uvádějí. + +## Jak jednou kótou popsat čtyři stejné díry Ø8? +- Čtyřikrát opsat Ø8 +- [x] 4× Ø8 a rozteč středů +- Jen jednu díru a ostatní nechat bez kóty +- R4 krát čtyři +> Počet, průměr a poloha středů stačí. Opakovat stejnou kótu u každé díry výkres zanáší. diff --git a/content/technicka-grafika/2-rocnik/07-pole.en.md b/content/technicka-grafika/2-rocnik/07-pole.en.md new file mode 100644 index 0000000..23f02f0 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/07-pole.en.md @@ -0,0 +1,6 @@ +--- +nazev: Rectangular and polar arrays +popis: Repeating holes in a row or on a circle. +nazev_cviceni: Quiz: arrays +nadpis_kvizu: Work out the spacing and the count +--- diff --git a/content/technicka-grafika/2-rocnik/07-pole.md b/content/technicka-grafika/2-rocnik/07-pole.md new file mode 100644 index 0000000..c0f8fd0 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/07-pole.md @@ -0,0 +1,80 @@ +--- +id: tg-2-pole +nazev: Obdélníkové a polární pole +popis: Opakování děr v řadě nebo na kružnici. +nazev_cviceni: Kvíz: pole +nadpis_kvizu: Dopočítejte rozteč a počet +--- + +# Výklad + +## Obdélníkové pole +*Řady a sloupce* + +- `_ARRAYRECT` rozkopíruje objekt do řádků a sloupců. +- Zadává se počet sloupců, počet řádků a rozteč, ne celkový rozměr. +- Rozteč 30 při třech sloupcích dá od první osy ke třetí 60 mm. +- Pole může zůstat svázané. Úprava jedné díry se propíše do ostatních. + +> Tip: tři díry v řadě mají dvě mezery. Celková délka je dvojnásobek rozteče, ne trojnásobek. + +## Polární pole +*Na kružnici* + +- `_ARRAYPOLAR` chce střed otáčení, počet prvků a úhel vyplnění. +- Čtyři díry po 360 stupních jsou po 90 stupních. +- Vzorová díra musí ležet na roztečné kružnici dřív, než pole spustíte. +- Střed pole je střed příruby, ne střed první díry. + +> Tip: roztečná kružnice Ø60 má poloměr 30. Vzorová díra leží třeba v bodě 30,0. + +## Roztečná kružnice se nekreslí tlustě +*Osa* + +- Kružnice, na které leží středy děr, je tenká čerchovaná osa. +- Není to hrana součásti a nepatří na OBRYS. +- Kótuje se jejím průměrem, často jako Ø60, a počtem děr. +- Samotné díry mají svou kótu průměru jen jednou. + +> Tip: na výkresu čtete 4× Ø8 na Ø60. Osm je díra, 60 je kružnice středů. + +## Zadání: čtyři díry na kružnici +*Cvičení* + +- Příruba je kružnice Ø80, střed 0,0. +- Vzorovou díru Ø8 dejte středem do 30,0. +- `_ARRAYPOLAR` kolem 0,0, čtyři prvky, 360 stupňů. +- Roztečnou kružnici Ø60 dejte na hladinu OSA. Ověřte `_DIST` mezi sousedními středy. + +> Tip: sousední středy svírají 90 stupňů na poloměru 30. Vzdálenost je 30 × √2, asi 42,4 mm. + + +# Kvíz + +## Tři díry v řadě mají rozteč 30. Jak daleko je první osa od třetí? +- 30 +- 90 +- [x] 60 +- 120 +> Mezi třemi dírami jsou dvě mezery. 2 × 30 = 60. + +## Kam se umísťuje vzorová díra pro pole na Ø60? +- Do středu příruby +- [x] Na poloměr 30 od středu +- 60 mm od středu +- Kamkoli, pole si poloměr dopočítá +> Ø60 znamená poloměr 30. Střed vzorové díry musí na této kružnici už ležet. + +## Čím je nakreslená kružnice středů děr? +- Tlustým obrysem +- [x] Tenkou čerchovanou osou +- Šrafou +- Kótovací čarou +> Není to hrana materiálu. Je to osa rozteče a patří na hladinu OSA. + +## Co znamená zápis 4× Ø8 na Ø60? +- Čtyři díry o průměru 60 +- [x] Čtyři díry Ø8, jejich středy leží na Ø60 +- Osazení 4 mm +- Zkosení 8 × 60 +> První průměr je díra, druhý je roztečná kružnice. diff --git a/content/technicka-grafika/2-rocnik/08-zadani-priruba.en.md b/content/technicka-grafika/2-rocnik/08-zadani-priruba.en.md new file mode 100644 index 0000000..1bb0ae0 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/08-zadani-priruba.en.md @@ -0,0 +1,6 @@ +--- +nazev: Assignment: a flange +popis: A drawing with an offset, a hole pattern, a hatch and dimensions. +nazev_cviceni: Quiz: the flange +nadpis_kvizu: Check the flange before you hand it in +--- diff --git a/content/technicka-grafika/2-rocnik/08-zadani-priruba.md b/content/technicka-grafika/2-rocnik/08-zadani-priruba.md new file mode 100644 index 0000000..6c75f73 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/08-zadani-priruba.md @@ -0,0 +1,80 @@ +--- +id: tg-2-zadani-priruba +nazev: Zadání: příruba +popis: Výkres s ekvidistantou, polem děr, šrafou a kótami. +nazev_cviceni: Kvíz: příruba +nadpis_kvizu: Zkontrolujte přírubu před odevzdáním +--- + +# Výklad + +## Tvar +*Rozměry* + +- Příruba Ø100, střed 0,0. Středová díra Ø40. +- Čtyři díry Ø9 na roztečné kružnici Ø70, po 90 stupních. +- Vnější hrana má zkosení 1 × 45°, které v tomto pohledu vidíte jako tenkou kružnici Ø98. +- Tloušťka příruby 12 mm se okótuje v druhém pohledu nebo v řezu. V čelním pohledu je kružnice. + +> Tip: rozteč Ø70 znamená vzorovou díru ve vzdálenosti 35 mm od středu. + +## Řez +*Aby byla vidět tloušťka* + +- Druhý pohled je řez přes osu. Ukáže tloušťku 12 a průchod díry Ø40. +- V řezu se šrafuje materiál příruby. Díra Ø40 zůstane prázdná. +- Díry Ø9 v tomto řezu nemusí vyjít všechny. Řezná rovina bere dvě protilehlé, nebo žádnou, podle úhlu. +- Zvolte řez rovinou, která prochází dvěma protilehlými dírami Ø9, ať je v řezu vidět i jejich průchod. + +> Tip: rovina pod úhlem 0 stupňů vezme díry na ose X. Jejich průchod je Ø9 skrz tloušťku 12. + +## Kóty a hladiny +*Jednou* + +- Kóty: Ø100, Ø40, 4× Ø9 na Ø70, tloušťka 12, zkosení 1 × 45°. +- OBRYS, OSA, SRAFA, KOTA, RAM. Typy čar jako ve druháku. +- Razítko: Příruba, 1:1, ocel. +- Formát A4 na šířku nebo A3, když se řez vedle čela nevejde čitelně. + +> Tip: kóta 4× Ø9 se neopakuje u každé díry. Roztečná kružnice má vlastní kótu Ø70. + +## Kontrola +*Před tiskem* + +- `_DIST` od středu k ose díry Ø9 je 35. +- Šrafa nezasahuje do díry Ø40 ani do děr Ø9. +- Vypnutí hladiny KOTA nechá čistý tvar. Čísla nezmizí z obrysu, protože na něm nemají být. +- Soubor priruba.dwg, jednotky milimetry, žádná kóta přepsaná ručně. + +> Tip: když kóta Ø100 ukazuje 99,9, vnější kružnice nemá průměr 100. Opravte kružnici, ne text. + + +# Kvíz + +## Jak daleko od středu leží osa díry na roztečné kružnici Ø70? +- 70 +- [x] 35 +- 9 +- 100 +> Rozteč se kótuje průměrem. Poloměr, tedy vzdálenost středu díry, je 35. + +## Jak jednou kótou popsat čtyři malé díry? +- Ø9 +- Ø70 +- [x] 4× Ø9 +- R4,5 +> Počet krát průměr. Poloha je až kóta roztečné kružnice. + +## Co se v řezu příruby nešrafuje? +- Těleso příruby +- [x] Průchod díry Ø40 +- Obrys +- Razítko se šrafuje vždy +> Šrafuje se materiál. Díra je prázdná, šrafa končí na její hraně. + +## Kóta vnějšího průměru ukazuje 99,9. Co uděláte? +- Přepíšete ji na 100 +- [x] Opravíte kružnici na Ø100 +- Změníte měřítko na 1:2 +- Smažete řez +> Kóta měří geometrii. Ruční text by zakryl, že kružnice je špatně. diff --git a/content/technicka-grafika/3-rocnik/01-pohledy.en.md b/content/technicka-grafika/3-rocnik/01-pohledy.en.md new file mode 100644 index 0000000..a25979f --- /dev/null +++ b/content/technicka-grafika/3-rocnik/01-pohledy.en.md @@ -0,0 +1,6 @@ +--- +nazev: Views and first-angle projection +popis: Front, top and side views the way they are drawn here. +nazev_cviceni: Quiz: views +nadpis_kvizu: Put the view in the right place +--- diff --git a/content/technicka-grafika/3-rocnik/01-pohledy.md b/content/technicka-grafika/3-rocnik/01-pohledy.md new file mode 100644 index 0000000..f247128 --- /dev/null +++ b/content/technicka-grafika/3-rocnik/01-pohledy.md @@ -0,0 +1,80 @@ +--- +id: tg-3-pohledy +nazev: Pohledy a evropské promítání +popis: Nárys, půdorys a bokorys tak, jak se kreslí u nás. +nazev_cviceni: Kvíz: pohledy +nadpis_kvizu: Umístěte pohled na správné místo +--- + +# Výklad + +## Tři základní pohledy +*Nárys* + +- Nárys je pohled zepředu. Vybírá se tak, aby ukázal tvar co nejlíp. +- Půdorys je pohled shora. Bokorys je pohled zleva. +- V evropském promítání leží půdorys pod nárysem, ne nad ním. +- Bokorys zleva leží vpravo od nárysu. Předmět si představte překlopený k vám. + +> Tip: americké promítání má půdorys nahoře. Na českém školním výkresu se nekreslí. + +## Co se v pohledu opakuje +*Stejná hrana* + +- Výška nárysu a výška bokorysu je stejné číslo. Šířka nárysu a šířka půdorysu taky. +- Hloubka půdorysu se rovná šířce bokorysu. Pomůže si 45stupňová pomocná čára. +- Skrytá hrana se v pohledu, kde není vidět, kreslí tenkou čárkovanou čarou. +- Hrana, která v daném pohledu splývá s obrysem, se podruhé nekreslí. + +> Tip: když je nárys vysoký 40, bokorys nemůže být vysoký 36. Někde chybí hrana nebo měřítko. + +## Zarovnání +*Jedna osa* + +- Pohledy se zarovnají. Svislé hrany nárysu a půdorysu leží na stejných svislicích. +- Vodorovné hrany nárysu a bokorysu leží na stejných vodorovných přímkách. +- Mezi pohledy nechte mezeru na kóty, třeba 30 až 40 mm. Pohledy se nesmí překrývat. +- Osa souměrnosti prochází všemi pohledy, ve kterých je součást souměrná. + +> Tip: `_XLINE` pod 45 stupňů přenese hloubku z půdorysu do bokorysu. Po dokreslení ji smažte nebo dejte na pomocnou hladinu. + +## Zadání: kvádr s osazením +*Cvičení* + +- Kvádr 60 × 40 × 30. Zepředu je na něm osazení: pravá část je o 10 mm nižší, v délce 20 mm. +- Nárys je 60 široký a 30 vysoký. Půdorys pod ním je 60 × 40. +- Bokorys vpravo je 40 široký a 30 vysoký. Osazení v něm vyjde jako skrytá nebo viditelná hrana podle směru. +- Pohledy zarovnejte. Výška 30 musí sedět v nárysu i v bokorysu. + +> Tip: osazení dlouhé 20 a nižší o 10 znamená, že v nárysu je horní hrana 40 mm v plné výšce a 20 mm ve výšce 20. + + +# Kvíz + +## Kam se v evropském promítání kreslí půdorys? +- Nad nárys +- [x] Pod nárys +- Vlevo od nárysu +- Místo bokorysu +> Evropské promítání klade pohled shora pod pohled zepředu. Nahoře by byl americký zvyk. + +## Co musí mít nárys a bokorys společné? +- Šířku +- [x] Výšku +- Hloubku jako jediné číslo bez převodu +- Razítko +> Výška předmětu je v obou pohledech stejná. Hloubka se přenáší mezi půdorysem a bokorysem. + +## Jak se kreslí hrana, kterou v daném pohledu není vidět? +- Tlustou plnou +- [x] Tenkou čárkovanou +- Čerchovanou osou +- Nezobrazuje se nikdy +> Skrytá hrana je čárkovaná. Vynechat ji smíte jen tam, kde by výkres zbytečně zanesla a norma to dovolí. + +## Kvádr je vysoký 30. Jak vysoký je jeho bokorys? +- 40 +- [x] 30 +- 60 +- Podle měřítka jinak +> Výška se mezi nárysem a bokorysem nemění. Měřítko je pro celý výkres jedno. diff --git a/content/technicka-grafika/3-rocnik/02-rezy.en.md b/content/technicka-grafika/3-rocnik/02-rezy.en.md new file mode 100644 index 0000000..3b30d5d --- /dev/null +++ b/content/technicka-grafika/3-rocnik/02-rezy.en.md @@ -0,0 +1,6 @@ +--- +nazev: Sections and cross-sections +popis: The cutting plane, the arrows and what gets hatched. +nazev_cviceni: Quiz: sections +nadpis_kvizu: Read the cutting plane +--- diff --git a/content/technicka-grafika/3-rocnik/02-rezy.md b/content/technicka-grafika/3-rocnik/02-rezy.md new file mode 100644 index 0000000..0845683 --- /dev/null +++ b/content/technicka-grafika/3-rocnik/02-rezy.md @@ -0,0 +1,80 @@ +--- +id: tg-3-rezy +nazev: Řezy a průřezy +popis: Řezná rovina, šipky a co se šrafuje. +nazev_cviceni: Kvíz: řezy +nadpis_kvizu: Přečtěte řeznou rovinu +--- + +# Výklad + +## Úplný řez +*Rovina A-A* + +- Řez ukáže vnitřek, který by jinak byl samá čárkovaná čára. +- Řezná rovina se v pohledu označí tlustou čerchovanou čarou se šipkami a písmeny, třeba A. +- Obraz řezu se popíše A-A. Šipky říkají, kterým směrem se díváme. +- Materiál proťatý rovinou se šrafuje. Prázdné díry ne. + +> Tip: šipky řezu míří na plochu, kterou uvidíte. Obrácené šipky otočí celý obraz. + +## Poloviční řez +*Souměrná součást* + +- Souměrná součást smí mít půl pohledu a půl řezu v jednom obrazu. +- Dělí je osa. Pohled bývá nad osou nebo vlevo, řez pod osou nebo vpravo. +- Na ose mezi polovinou pohledu a řezu se nekreslí tlustý obrys. +- Skryté hrany v neřezané polovině se často vynechají, protože řez už vnitřek ukázal. + +> Tip: hřídel s dírou na ose je typický poloviční řez. Nahoře obrys, dole díra a šrafa. + +## Co řezem není +*Průřez* + +- Průřez ukáže jen tvar v řezné rovině, bez toho, co leží za ní. +- Řez ukáže i hrany za rovinou. Proto je na řezu víc čar než na průřezu. +- Tenký průřez se smí nakreslit přímo do pohledu, pootočený na místě. +- Žebro řezané podélně se nešrafuje ani v úplném řezu. + +> Tip: když za rovinou vidíte další osazení, je to řez. Když je tam jen vyšrafovaný profil, je to průřez. + +## Zadání: pouzdro +*Cvičení* + +- Pouzdro je válec Ø50, díra Ø30, délka 40. Nakreslete nárys jako úplný řez A-A osou. +- V řezu vyšrafujte mezikruží. Díra Ø30 zůstane prázdná po celé délce 40. +- Šipky A umístěte do půdorysu nebo do místa, odkud je rovina jasná. +- Kóty Ø50, Ø30 a 40. Hřídel to není, šrafa tu být má. + +> Tip: tloušťka stěny je (50 − 30) / 2 = 10 mm. V řezu je vidět vlevo i vpravo od díry. + + +# Kvíz + +## Co říkají šipky u řezné roviny? +- Tloušťku čáry +- [x] Směr pohledu na řez +- Měřítko šrafy +- Počet děr +> Obraz řezu je to, co bychom viděli ve směru šipek. Obrácené šipky obraz otočí. + +## Šrafuje se díra Ø30 v řezu pouzdra? +- Ano, je uprostřed +- [x] Ne, není tam materiál +- Jen v měřítku 1:2 +- Jen když je pouzdro z oceli +> Šrafuje se stěna. Průchozí díra zůstane bílá. + +## Čím se liší průřez od řezu? +- Průřez se nešrafuje +- [x] Průřez neukazuje hrany za řeznou rovinou +- Řez nemá písmena +- Průřez je vždy v měřítku 2:1 +> Řez dokreslí i to, co leží za rovinou. Průřez je jen proťatý tvar. + +## Kam se u souměrného polovičního řezu kreslí řez? +- Přes celý obraz +- [x] Jen na jednu stranu od osy +- Mimo pohled, bez označení +- Do razítka +> Druhá polovina zůstane pohledem. Dělí je osa, ne tlustá hrana. diff --git a/content/technicka-grafika/3-rocnik/03-bloky.en.md b/content/technicka-grafika/3-rocnik/03-bloky.en.md new file mode 100644 index 0000000..f9b3ccc --- /dev/null +++ b/content/technicka-grafika/3-rocnik/03-bloky.en.md @@ -0,0 +1,6 @@ +--- +nazev: Blocks +popis: A repeated part as one object with a base point. +nazev_cviceni: Quiz: blocks +nadpis_kvizu: Insert the block correctly +--- diff --git a/content/technicka-grafika/3-rocnik/03-bloky.md b/content/technicka-grafika/3-rocnik/03-bloky.md new file mode 100644 index 0000000..933c73f --- /dev/null +++ b/content/technicka-grafika/3-rocnik/03-bloky.md @@ -0,0 +1,80 @@ +--- +id: tg-3-bloky +nazev: Bloky +popis: Opakovaný díl jako jeden objekt se základním bodem. +nazev_cviceni: Kvíz: bloky +nadpis_kvizu: Vložte blok správně +--- + +# Výklad + +## K čemu blok je +*Jeden díl* + +- Blok je pojmenovaná skupina objektů, která se vkládá jako jeden kus. +- Šroub, značka nebo razítko se nekreslí pokaždé znovu. +- Úprava definice bloku se propíše do všech vložení. Kopie by zůstaly staré. +- Blok patří do výkresu. `_WBLOCK` ho navíc uloží do vlastního souboru. + +> Tip: čtyři stejné šrouby jsou čtyři vložení jednoho bloku, ne čtyři překreslené šrouby. + +## Základní bod +*Kam se chytá* + +- `_BLOCK` se zeptá na jméno, základní bod a objekty. +- Základní bod je místo, za které budete blok vkládat. U šroubu třeba průsečík osy a dosedací plochy. +- Špatný základní bod posune každé vložení o stejnou chybu. +- Objekty bloku nedávejte na hladinu 0, pokud nemají přebírat barvu místa vložení. Na škole nechte jejich vlastní hladiny. + +> Tip: základní bod chytejte úchopem. Bod „někde u šroubu“ se při vložení mine o několik milimetrů. + +## Vložení +*Místo, měřítko, úhel* + +- `_INSERT` vybere jméno, bod vložení, měřítko a otočení. +- Měřítko 1 a úhel 0 vloží blok tak, jak byl nakreslen. +- Měřítko 2 ho zvětší. Na výkresu 1:1 se měřítko bloku nemění, mění se až měřítko výřezu. +- Rozbití bloku příkazem `_EXPLODE` z něj zase udělá obyčejné čáry. Dělejte to jen když máte měnit jediné vložení. + +> Tip: rozbitý blok se už neaktualizuje s definicí. Rozbijte ho, jen když opravdu chcete výjimku. + +## Zadání: šroub M8 jako blok +*Cvičení* + +- V nárysu nakreslete zjednodušený šroub: dřík Ø8 dlouhý 20 a hlava široká 13 vysoká 5,5. +- Základní bod dejte na osu, do dosedací plochy pod hlavou. +- Blok pojmenujte SROUB-M8 a vložte ho dvakrát, po 40 mm. +- Změňte délku dříku v definici na 25. Obě vložení se musí prodloužit. + +> Tip: dosedací plocha je rozhraní hlavy a dříku. Od ní se měří, jak hluboko šroub sedí. + + +# Kvíz + +## Čím se liší blok od obyčejné kopie? +- Ničím +- [x] Úprava definice se propíše do všech vložení +- Blok nejde otočit +- Kopie zabere méně místa +> Kopie jsou nezávislé. Vložení bloku sdílejí jednu definici. + +## Co je základní bod bloku? +- Střed razítka +- [x] Bod, za který se blok vkládá +- Vždy 0,0 výkresu +- Pravý horní roh objektů +> Špatný základní bod posune každé vložení. Chytejte ho úchopem na osu nebo na dosedací plochu. + +## Kdy blok rozbít příkazem `_EXPLODE`? +- Vždy po vložení +- [x] Jen když jedno vložení má být výjimka +- Aby se zmenšil soubor +- Před každým tiskem +> Rozbitý blok se s definicí už neaktualizuje. Ostatní vložení nechte celé. + +## Měřítko vložení 1 na výkresu 1:1 znamená? +- Blok se kreslí poloviční +- [x] Blok se vloží ve velikosti, v jaké byl nakreslen +- Blok se převrátí +- Měřítko razítka se změní na 1:2 +> Měřítko výkresu řeší až výřez na papíře. Blok součásti nechte v měřítku 1. diff --git a/content/technicka-grafika/3-rocnik/04-atributy-a-kusovnik.en.md b/content/technicka-grafika/3-rocnik/04-atributy-a-kusovnik.en.md new file mode 100644 index 0000000..b19bfc1 --- /dev/null +++ b/content/technicka-grafika/3-rocnik/04-atributy-a-kusovnik.en.md @@ -0,0 +1,6 @@ +--- +nazev: Attributes and the parts list +popis: Text in a block that changes with every insert. +nazev_cviceni: Quiz: attributes +nadpis_kvizu: Read the parts list +--- diff --git a/content/technicka-grafika/3-rocnik/04-atributy-a-kusovnik.md b/content/technicka-grafika/3-rocnik/04-atributy-a-kusovnik.md new file mode 100644 index 0000000..03df34e --- /dev/null +++ b/content/technicka-grafika/3-rocnik/04-atributy-a-kusovnik.md @@ -0,0 +1,80 @@ +--- +id: tg-3-atributy +nazev: Atributy a kusovník +popis: Text v bloku, který se mění s každým vložením. +nazev_cviceni: Kvíz: atributy +nadpis_kvizu: Přečtěte kusovník +--- + +# Výklad + +## Atribut je popisek bloku +*Značka* + +- Atribut je text uložený v bloku. Při každém vložení se zeptá znovu. +- `_ATTDEF` založí značku, výzvu a výchozí hodnotu. Značka je třeba OZN. +- Do bloku se atribut vloží spolu s geometrií. Samotný atribut na výkresu nic neopakuje. +- Hodnotu už vloženého bloku změníte `_ATTEDIT` nebo dvojklikem, ne přepsáním čáry. + +> Tip: značka OZN není text, který se tiskne. Tiskne se hodnota, třeba 1 nebo M8×20. + +## Kusovník +*Co je na výkresu* + +- Kusovník je tabulka nad razítkem. Řádek je jedna položka sestavy. +- Sloupce bývají pozice, název, počet, materiál nebo norma a rozměr. +- Pozice v kolečku na výkresu musí souhlasit s řádkem tabulky. +- Počet se počítá z vložení, ne od oka. Dva stejné šrouby jsou počet 2, ne dva řádky. + +> Tip: tabulku `_TABLE` dejte na hladinu TEXT nebo RAM. Čáry tabulky nejsou obrys součásti. + +## Pořadí +*Nejdřív definice* + +- Nejdřív nakreslete díl, přidejte atribut, teprve pak z toho udělejte blok. +- Atribut přidaný až po vytvoření bloku se do starých vložení sám nedostane. +- `_BATTMAN` nebo nové definování bloku vložení srovná. Na škole je jistější blok založit znovu. +- Výchozí hodnota atributu je nápověda. U šroubu třeba M8×20, a při vložení ji smíte změnit. + +> Tip: prázdná hodnota v kusovníku znamená, že jste při vložení výzvu odklepli bez textu. + +## Zadání: tři položky +*Cvičení* + +- Sestava má přírubu (pozice 1, 1 kus, ocel), šroub M8×20 (pozice 2, 4 kusy) a matici M8 (pozice 3, 4 kusy). +- Šroub je blok s atributem OZN. Čtyři vložení mají stejnou hodnotu. +- Kusovník nad razítkem má tři řádky. Pozice 2 má počet 4, ne 1. +- Odkazové čáry z koleček 1, 2 a 3 míří na správný díl a nekříží se bez důvodu. + +> Tip: čtyři matice jsou pořád jedna položka. Do tabulky nepište čtyři řádky se stejným šroubem. + + +# Kvíz + +## Co se při vložení bloku s atributem ptá znovu? +- Barva hladiny +- [x] Hodnota atributu, třeba označení +- Základní bod definice +- Měřítko výkresu +> Geometrie bloku zůstává. Mění se text, který jste založili jako atribut. + +## Čtyři stejné šrouby v kusovníku jsou? +- Čtyři řádky +- [x] Jeden řádek s počtem 4 +- Pozice 1 až 4 +- Atribut bez bloku +> Stejný díl je jedna položka. Počet říká, kolikrát je vložený. + +## Kdy se atribut musí založit? +- Až po tisku +- [x] Před vytvořením bloku, spolu s geometrií +- Jen v razítku +- Atribut nejde uložit do bloku +> Atribut přidaný později se do existující definice sám nepřidá. + +## Kam patří kusovník? +- Přes nárys +- [x] Nad razítko +- Na hladinu OBRYS +- Mimo rámeček +> Čte se spolu s razítkem. Čáry tabulky nejsou hrany součásti. diff --git a/content/technicka-grafika/3-rocnik/05-rozvrzeni-a-vyrezy.en.md b/content/technicka-grafika/3-rocnik/05-rozvrzeni-a-vyrezy.en.md new file mode 100644 index 0000000..14a29ca --- /dev/null +++ b/content/technicka-grafika/3-rocnik/05-rozvrzeni-a-vyrezy.en.md @@ -0,0 +1,6 @@ +--- +nazev: Layouts and viewports +popis: The sheet of paper that looks into the model. +nazev_cviceni: Quiz: viewports +nadpis_kvizu: Set the viewport scale +--- diff --git a/content/technicka-grafika/3-rocnik/05-rozvrzeni-a-vyrezy.md b/content/technicka-grafika/3-rocnik/05-rozvrzeni-a-vyrezy.md new file mode 100644 index 0000000..0f26aa9 --- /dev/null +++ b/content/technicka-grafika/3-rocnik/05-rozvrzeni-a-vyrezy.md @@ -0,0 +1,80 @@ +--- +id: tg-3-rozvrzeni +nazev: Rozvržení a výřezy +popis: Papír, na kterém leží pohled do modelu. +nazev_cviceni: Kvíz: výřezy +nadpis_kvizu: Nastavte měřítko výřezu +--- + +# Výklad + +## Model a papír +*Dva prostory* + +- V modelu je součást v milimetrech 1:1. Papír je rozvržení, záložka nad příkazovým řádkem. +- Na papír přijde rámeček, razítko a výřez. Výřez je okno do modelu. +- Dvojklik dovnitř výřezu přepne do modelu. Dvojklik ven vrátí papír. +- Kóty součásti nechte v modelu. Razítko kreslete v rozvržení, ať má na papíře stálou velikost. + +> Tip: rámeček nakreslený v modelu se při změně měřítka výřezu zmenší spolu se součástí. Proto patří na papír. + +## Měřítko výřezu +*1:2 na papíře* + +- Výřez má vlastní měřítko. Součást v modelu zůstává 1:1. +- Měřítko 1:2 znamená, že 1 mm na papíře je 2 mm na součásti. +- Ve stavovém řádku výřezu se měřítko vybírá ze seznamu. Starší zápis je `_ZOOM` a `1/2xp`. +- Výřez zamkněte, ať kolečko myši měřítko omylem nerozhodí. + +> Tip: nezamčený výřez je nejčastější důvod, proč ráno výkres není 1:1, i když včera byl. + +## Co je vidět +*Hladiny ve výřezu* + +- Hladinu lze ve výřezu zmrazit a v modelu ji nechat. Pomocné čáry tak na papír nejdou. +- Rámeček výřezu dejte na hladinu VYREZ a tu před tiskem vypněte. Na papíře zůstane jen obsah. +- Jeden výkres smí mít víc výřezů: čelo, řez, detail v jiném měřítku. +- Detail 2:1 je druhý výřez. V razítku se pak píše měřítko hlavní, detail má vlastní popisku. + +> Tip: detail zvětšený 2:1 nekreslete znovu. Je to stejný model v druhém výřezu. + +## Zadání: A3 a měřítko 1:2 +*Cvičení* + +- Rozvržení nastavte na A3 na šířku. Rámeček a razítko nakreslete v rozvržení. +- Výřez ukazuje přírubu Ø100 v měřítku 1:2. Na papíře má mít průměr 50 mm. +- Výřez zamkněte. Hladinu jeho rámečku vypněte. +- `_DIST` na papíře přes průměr příruby má ukázat 50, ne 100. + +> Tip: měření v rozvržení, mimo výřez, čte milimetry papíru. 100 mm součásti v měřítku 1:2 je na papíře 50 mm. + + +# Kvíz + +## Kde se kreslí tvar součásti v měřítku 1:1? +- V rozvržení +- [x] V modelu +- V razítku +- V hladině VYREZ +> Model zůstává skutečný. Měřítko pro papír nastavuje až výřez. + +## Součást Ø100 ve výřezu 1:2 má na papíře průměr? +- 100 mm +- [x] 50 mm +- 200 mm +- 1 mm +> 1:2 zmenšuje na polovinu. Kóta ve modelu pořád říká 100. + +## Proč výřez zamknout? +- Aby nešel tisknout +- [x] Aby kolečko myši nezměnilo měřítko +- Zamknutí smaže model +- Aby šel rámeček smazat +> Nezamčený výřez se při zoomu rozhodí a výkres už není v měřítku z razítka. + +## Kam patří rámeček a razítko? +- Do modelu, na hladinu OBRYS +- [x] Do rozvržení +- Do definice bloku šroubu +- Mimo papír +> V rozvržení mají stálou velikost bez ohledu na měřítko výřezu. diff --git a/content/technicka-grafika/3-rocnik/06-tisk.en.md b/content/technicka-grafika/3-rocnik/06-tisk.en.md new file mode 100644 index 0000000..2111b75 --- /dev/null +++ b/content/technicka-grafika/3-rocnik/06-tisk.en.md @@ -0,0 +1,6 @@ +--- +nazev: Plotting and PDF +popis: The sheet, the plot area and a black pen. +nazev_cviceni: Quiz: plotting +nadpis_kvizu: Choose the plotter settings +--- diff --git a/content/technicka-grafika/3-rocnik/06-tisk.md b/content/technicka-grafika/3-rocnik/06-tisk.md new file mode 100644 index 0000000..9294a6f --- /dev/null +++ b/content/technicka-grafika/3-rocnik/06-tisk.md @@ -0,0 +1,80 @@ +--- +id: tg-3-tisk +nazev: Tisk a PDF +popis: Papír, oblast tisku a černobílé pero. +nazev_cviceni: Kvíz: tisk +nadpis_kvizu: Vyberte nastavení plotru +--- + +# Výklad + +## Plotr +*Na papír nebo do PDF* + +- `_PLOT` tiskne aktuální rozvržení. Oblast tisku nechte Layout, ne Extents. +- Tiskárna DWG To PDF uloží výkres, který jde odevzdat bez posunu měřítka. +- Formát papíru musí sedět s rozvržením. A3 v tisku a A4 v rozvržení ořízne razítko. +- Měřítko tisku z rozvržení je 1:1. Měřítko součásti už drží výřez. + +> Tip: tisk 1:2 z rozvržení, které už má výřez 1:2, zmenší výkres podruhé. Na papíře bude 1:4. + +## Pero +*Černá čára* + +- Tabulka per monochrome.ctb tiskne vše černě a tloušťku bere z hladin. +- Barevný tisk ve škole nechte jen na náhled. Odevzdává se černobílý výkres. +- Tloušťka obrysu 0,5 mm a tenká čára 0,25 mm musí být v tabulce nebo na hladině vidět. +- Náhled tisku ukáže, jestli razítko neleze z papíru. Bez náhledu netiskněte. + +> Tip: když je na PDF všechno vlásečnicové, tloušťky hladin se netisknou. V `_PLOT` je zapněte. + +## Co do PDF nepatří +*Čistý výkres* + +- Rámeček výřezu je vypnutý. Pomocné konstrukce taky. +- Kóty jsou čitelné a nejsou přes šrafu, pokud to šlo jinak. +- PDF pojmenujte jako výkres, třeba priruba-A3.pdf, ne Plot1.pdf. +- Vytiskněte i kontrolní kópii na papír, když učitel odevzdání na papíře chce. + +> Tip: otevřete PDF a změřte rámeček A3. Má mít 420 × 297 mm. Jinak je špatně formát nebo měřítko tisku. + +## Zadání: PDF příruby +*Cvičení* + +- Rozvržení A3, výřez 1:1 nebo 1:2 podle toho, co se vejde i s kótami. +- `_PLOT`, tiskárna DWG To PDF, oblast Layout, měřítko 1:1, pero monochrome.ctb. +- Náhled zkontroluje, že razítko je celé a šrafa není černá plocha. +- Soubor uložte jako priruba-A3.pdf vedle dwg. + +> Tip: hustá šrafa v náhledu znamená velké měřítko vzoru nebo malé. Vraťte se do `_HATCH` a rozteč zvětšete. + + +# Kvíz + +## Jaké měřítko tisku se dává při tisku rozvržení? +- Stejné jako měřítko výřezu, znovu +- [x] 1:1 +- Fit to paper +- 2:1, ať je výkres větší +> Měřítko součásti už má výřez. Tisk papíru je 1:1, jinak se měřítko zdvojí. + +## K čemu je monochrome.ctb? +- Zvětší písmo +- [x] Tiskne černě a dodrží tloušťky +- Zamkne výřez +- Smaže hladinu OSA +> Barevné čáry na obrazovce jsou pro práci. Odevzdává se černý výkres s tlustým obrysem. + +## Proč nenechat oblast tisku na Extents? +- Extents je rychlejší +- [x] Vzalo by i objekty mimo papír a rozhodilo by měřítko +- Extents netiskne kóty +- Extents vyžaduje 3D +> Layout tiskne papír tak, jak jste ho složili. Extents hledá vše nakreslené a měřítko si dopočítá samo. + +## Co má mít rámeček A3 v hotovém PDF? +- 210 × 297 mm +- [x] 420 × 297 mm +- 100 × 70 mm +- Libovolný rozměr, rozhoduje obrazovka +> A3 je 420 × 297 mm. Když PDF vyjde menší, je špatně formát papíru nebo měřítko tisku. diff --git a/content/technicka-grafika/3-rocnik/07-izometrie.en.md b/content/technicka-grafika/3-rocnik/07-izometrie.en.md new file mode 100644 index 0000000..609e526 --- /dev/null +++ b/content/technicka-grafika/3-rocnik/07-izometrie.en.md @@ -0,0 +1,6 @@ +--- +nazev: Isometric drawing +popis: A pictorial view that does not shrink edges the way perspective does. +nazev_cviceni: Quiz: isometrics +nadpis_kvizu: Pick the axis and the ellipse +--- diff --git a/content/technicka-grafika/3-rocnik/07-izometrie.md b/content/technicka-grafika/3-rocnik/07-izometrie.md new file mode 100644 index 0000000..3dda985 --- /dev/null +++ b/content/technicka-grafika/3-rocnik/07-izometrie.md @@ -0,0 +1,80 @@ +--- +id: tg-3-izometrie +nazev: Izometrie +popis: Názorný obraz, ve kterém se rozměry nezkreslují stejně jako v perspektivě. +nazev_cviceni: Kvíz: izometrie +nadpis_kvizu: Vyberte osu a elipsu +--- + +# Výklad + +## Tři osy +*120 stupňů* + +- Izometrie má tři osy po 120 stupních. Svislá zůstává svislá. +- Délky se nanášejí ve skutečné velikosti. Není to perspektiva, hrany se do dálky nezkracují. +- `_ISODRAFT` přepne kreslicí rovinu. F5 cyklí mezi levou, pravou a horní rovinou. +- Orto v izometrii drží čáry podél aktuálních os, ne podél obyčejného X a Y. + +> Tip: než nakreslíte hranu, podívejte se, která izorovina svítí. Čára v špatné rovině neleží na stěně. + +## Kružnice je elipsa +*Isokružnice* + +- Kružnice v izometrii se kreslí jako elipsa. `_ELLIPSE` a volba Isocircle. +- Nejdřív F5 na rovinu, ve které kružnice leží. Horní díra chce horní rovinu. +- Průměr se zadává skutečný. Elipsa ho zobrazí zkoseně, kóta zůstává Ø. +- Obyčejná kružnice `_CIRCLE` v izometrii je chyba. Na papíře by byla pořád kulatá a neladila by s osami. + +> Tip: díra na horní stěně a díra na přední stěně jsou dvě různě natočené elipsy. F5 mezi nimi přepněte. + +## K čemu izometrie je +*Názornost* + +- Izometrie nenahrazuje nárys a řez. Výrobu řídí okótované pohledy. +- Na školní práci bývá izometrie v rohu, bez kót nebo jen s hlavními. +- Skryté hrany se v izometrii často vynechají, ať obraz zůstane čitelný. +- Tlustý obrys nechte na viditelných hranách. Osy děr smíte naznačit tenčeji. + +> Tip: když se izometrie neshoduje s nárysem, platí nárys. Izometrii překreslete. + +## Zadání: kvádr s dírou +*Cvičení* + +- V izometrii nakreslete kvádr 60 × 40 × 30. +- Na horní stěně je díra Ø16 uprostřed. Použijte isokružnici v horní rovině. +- Soustředně naznačte tenčí elipsu, jen pokud by šlo o zahloubení. Tady zahloubení není. +- Porovnejte rozměry s nárysem téhož kvádru. 60, 40 a 30 musí sedět na obou. + +> Tip: střed horní stěny najdete v polovině šířky a hloubky. Elipsa se středem v rohu je špatně. + + +# Kvíz + +## Čím se v izometrii kreslí kružnice? +- `_CIRCLE` +- [x] `_ELLIPSE` s volbou Isocircle +- `_ARC` o 360 stupňů +- `_POLYGON` +> Obyčejná kružnice zůstane kulatá a s izometrickými osami neladí. + +## Co přepíná klávesa F5 při zapnutém `_ISODRAFT`? +- Hladiny +- [x] Izorovina: levá, pravá, horní +- Měřítko tisku +- Orto +> Každá stěna má vlastní natočení elipsy. F5 vybere stěnu, na kterou kreslíte. + +## Zkracují se v izometrii hrany směrem dozadu? +- Ano, jako v perspektivě +- [x] Ne, délky se nanášejí skutečné +- Jen svislé +- Jen při měřítku 1:2 +> Izometrie není perspektiva. Hrana dlouhá 60 mm se nanese jako 60 mm. + +## Co platí, když izometrie nesedí s nárysem? +- Platí izometrie, je názornější +- [x] Platí okótovaný nárys +- Oba se nechají a učitel si vybere +- Smaže se razítko +> Výrobu určují pohledy a kóty. Izometrie je doplněk a musí se jim přizpůsobit. diff --git a/content/technicka-grafika/3-rocnik/08-zadani-pouzdro.en.md b/content/technicka-grafika/3-rocnik/08-zadani-pouzdro.en.md new file mode 100644 index 0000000..64e0b7f --- /dev/null +++ b/content/technicka-grafika/3-rocnik/08-zadani-pouzdro.en.md @@ -0,0 +1,6 @@ +--- +nazev: Assignment: a sleeve +popis: A sectioned front view, a second view, dimensions and an A3 plot. +nazev_cviceni: Quiz: the sleeve +nadpis_kvizu: Check the sleeve drawing +--- diff --git a/content/technicka-grafika/3-rocnik/08-zadani-pouzdro.md b/content/technicka-grafika/3-rocnik/08-zadani-pouzdro.md new file mode 100644 index 0000000..f6d3a4f --- /dev/null +++ b/content/technicka-grafika/3-rocnik/08-zadani-pouzdro.md @@ -0,0 +1,80 @@ +--- +id: tg-3-zadani-pouzdro +nazev: Zadání: pouzdro +popis: Nárys v řezu, půdorys, kóty a tisk na A3. +nazev_cviceni: Kvíz: pouzdro +nadpis_kvizu: Zkontrolujte výkres pouzdra +--- + +# Výklad + +## Tvar +*Rozměry* + +- Pouzdro je dutý válec. Vnější Ø60, díra Ø36, délka 80. +- Na pravém konci je osazení: vnější Ø48 v délce 20. Díra Ø36 jde skrz. +- Na Ø60 jsou čtyři díry M6 do hloubky 12, na roztečné kružnici Ø48. Nejsou průchozí. +- Závit M6 se v nárysu kreslí zjednodušeně: velký průměr tenkou čarou, malý tlustou, v délce 12 mm. + +> Tip: rozteč děr M6 je Ø48, tedy 24 mm od osy. Hloubka 12 není délka pouzdra. + +## Pohledy +*Řez a bok* + +- Nárys je úplný řez osou. Šrafuje se stěna, díra Ø36 zůstane prázdná. +- Půdorys nebo bokorys ukáže čtyři díry M6 na Ø48. Jednu okótujte 4× M6 a hloubku 12. +- Evropské promítání: druhý pohled pod nárysem nebo vpravo, zarovnaný. +- Izometrii přidejte vpravo dole, bez kót, ať je tvar jasný. + +> Tip: v řezu osou jsou vidět dvě protilehlé díry M6, pokud rovina A-A jimi prochází. Hloubka 12 se kótuje tady. + +## Výkres +*A3* + +- Model 1:1. Rozvržení A3, měřítko výřezu 1:1, tisk 1:1, monochrome.ctb. +- Hladiny OBRYS, OSA, SKRYTE, SRAFA, KOTA, ZAVIT, RAM. +- Razítko: Pouzdro, ocel, 1:1. Kóty Ø60, Ø36, Ø48, délka 80, osazení 20 a Ø48, 4× M6 hloubka 12. +- Závitová hladina je tenká. Není to obrys válce. + +> Tip: malý průměr závitu M6 je zhruba 5 mm. Tenká čára velkého průměru je 6 mm. Neměňte je za Ø36. + +## Kontrola +*Odevzdání* + +- Stěna v řezu má tloušťku (60 − 36) / 2 = 12 mm. +- Osazení dlouhé 20 nešrafujte jinak než zbytek stěny. Je to pořád jeden kus. +- PDF priruba nahraďte souborem pouzdro-A3.pdf. Razítko je celé na papíře. +- Žádná kóta není přepsaná. `_DIST` přes vnější průměr dá 60. + +> Tip: dva kusy by měly opačný sklon šrafy. Tady je kus jeden, šrafa má jeden směr. + + +# Kvíz + +## Jaká je tloušťka stěny pouzdra Ø60 s dírou Ø36? +- 24 mm +- [x] 12 mm +- 36 mm +- 6 mm +> Rozdíl průměrů je 24 mm materiálu na obě strany. Na jednu stěnu připadá 12 mm. + +## Jak se zjednodušeně kreslí závit M6? +- Obě čáry tlusté +- [x] Velký průměr tenkou čarou, malý tlustou +- Jen osou +- Šrafou ANSI31 +> Tenká čára je velký průměr 6 mm. Tlustá je malý průměr, u M6 zhruba 5 mm. + +## Kam patří čtyři díry M6? +- Průchozí skrz Ø36 +- [x] Do hloubky 12 mm na roztečné kružnici Ø48 +- Na osazení Ø48 jako drážka +- Do razítka +> Nejsou to průchozí díry hlavní dutiny. Hloubka 12 a rozteč Ø48 jsou v zadání. + +## Proč má šrafa pouzdra jeden směr? +- Dva směry jsou zakázané vždy +- [x] Je to jeden kus +- Řez je poloviční +- Měřítko je 1:2 +> Opačný sklon by říkal, že se dotýkají dva díly. Osazení je pořád totéž pouzdro. diff --git a/content/technicka-grafika/4-rocnik/01-prostor-3d.en.md b/content/technicka-grafika/4-rocnik/01-prostor-3d.en.md new file mode 100644 index 0000000..b7db361 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/01-prostor-3d.en.md @@ -0,0 +1,6 @@ +--- +nazev: Model space in 3D +popis: Axes, the user coordinate system and orbiting the model. +nazev_cviceni: Quiz: space +nadpis_kvizu: Find the axis and the AutoCAD edition +--- diff --git a/content/technicka-grafika/4-rocnik/01-prostor-3d.md b/content/technicka-grafika/4-rocnik/01-prostor-3d.md new file mode 100644 index 0000000..2c94303 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/01-prostor-3d.md @@ -0,0 +1,80 @@ +--- +id: tg-4-prostor +nazev: Modelový prostor ve 3D +popis: Osy, uživatelský souřadný systém a obíhání kolem modelu. +nazev_cviceni: Kvíz: prostor +nadpis_kvizu: Najděte osu a verzi AutoCADu +--- + +# Výklad + +## Tři osy +*X, Y, Z* + +- K ose X a Y přibude Z. Na obrazovce při startu míří Z k vám. +- Pravidlo pravé ruky: prsty od X k Y, palec ukazuje Z. +- `_BOX` a další tělesa potřebují plný AutoCAD. Verze LT tělesa nevytvoří. +- 2D výkres z dřívějška zůstává. 3D model je druhý soubor, nebo stejný model v jiném pohledu. + +> Tip: když příkaz `_BOX` AutoCAD nezná, máte LT. Tělesa v ní nejdou. + +## Pohled +*Orbit* + +- ViewCube v rohu nebo `_ORBIT` otáčí pohled. Model se neotáčí, otáčí se kamera. +- Přední, horní a levý pohled odpovídají nárysu, půdorysu a bokorysu. +- `_UCS` posune a natočí souřadnice. Nová tělesa vznikají vůči aktuálnímu UCS. +- `_UCS` a volba World vrátí původní osy. Ztracený UCS je nejčastější zmatek ve 3D. + +> Tip: než vysunete profil, zkontrolujte ikonu os. Z má trčet z plochy, kterou vysouváte. + +## Drát a stínování +*Ať něco vidíte* + +- Drátový model ukáže všechny hrany, i zadní. Na začátku se v něm líp trefuje. +- Stínovaný pohled schová zadní hrany. Přepíná se `_VSCURRENT` nebo nabídkou pohledu. +- Zoom kolečkem pořád platí. Výřez na papír přijde až v lekci o výkresu z modelu. +- Jednotky zůstávají milimetry. Těleso 60 × 40 × 30 je v tomtéž souboru jako 2D profil. + +> Tip: ztracený model najdete `_ZOOM` Extents. Často leží daleko od počátku, protože UCS byl posunutý. + +## Zadání: tři osy a kvádr +*Cvičení* + +- V novém souboru v milimetrech nakreslete `_BOX` se základnou 60 × 40 a výškou 30, roh v 0,0,0. +- Přepněte na přední, horní a levý pohled. V každém má být vidět jiná dvojice rozměrů. +- `_UCS` World a `_DIST` mezi dvěma rohy podél X má dát 60. +- Uložte jako kvadr-60x40x30.dwg. LT na tento soubor nestačí. + +> Tip: přední pohled ukáže 60 × 30, horní 60 × 40, levý 40 × 30. + + +# Kvíz + +## Který AutoCAD umí tělesa `_BOX`? +- Každá verze včetně LT +- [x] Plný AutoCAD, ne LT +- Jen verze na Mac bez příkazů +- Žádný, tělesa jsou v jiném programu +> LT je 2D. Příkaz `_BOX` v ní není. + +## Co dělá `_ORBIT`? +- Otočí těleso v souřadnicích +- [x] Otočí pohled kamery +- Nastaví měřítko tisku +- Smaže osu Z +> Model zůstane. Změní se, odkud se na něj díváte. + +## Co vrátí souřadnice do původního směru? +- `_ZOOM` Extents +- [x] `_UCS` a volba World +- Vypnutí hladiny 0 +- `_PLOT` +> Posunutý UCS nechá nová tělesa „ležet na boku“. World je výchozí osy výkresu. + +## Co ukáže přední pohled na kvádr 60 × 40 × 30, když 30 je výška? +- 60 × 40 +- [x] 60 × 30 +- 40 × 30 +- Jen osu Z +> Přední pohled je šířka a výška. Hloubka 40 je v půdorysu a v bokorysu. diff --git a/content/technicka-grafika/4-rocnik/02-telesa-a-vysunuti.en.md b/content/technicka-grafika/4-rocnik/02-telesa-a-vysunuti.en.md new file mode 100644 index 0000000..8684db3 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/02-telesa-a-vysunuti.en.md @@ -0,0 +1,6 @@ +--- +nazev: Solids and extrude +popis: A box, a cylinder and a profile pulled up by a height. +nazev_cviceni: Quiz: extrude +nadpis_kvizu: Pick the profile and the height +--- diff --git a/content/technicka-grafika/4-rocnik/02-telesa-a-vysunuti.md b/content/technicka-grafika/4-rocnik/02-telesa-a-vysunuti.md new file mode 100644 index 0000000..d115dcf --- /dev/null +++ b/content/technicka-grafika/4-rocnik/02-telesa-a-vysunuti.md @@ -0,0 +1,80 @@ +--- +id: tg-4-vysunuti +nazev: Tělesa a vysunutí +popis: Kvádr, válec a profil tažený o výšku. +nazev_cviceni: Kvíz: vysunutí +nadpis_kvizu: Vyberte profil a výšku +--- + +# Výklad + +## Hotová tělesa +*Kvádr a válec* + +- `_BOX` chce roh nebo střed základny, půdorys a výšku. +- `_CYLINDER` chce střed, poloměr nebo průměr a výšku. +- Výška se měří ve směru osy Z aktuálního UCS. +- Tato tělesa jsou plná. Nejsou to stěny z ploch. + +> Tip: válec Ø40 a výška 25 má v příkazu buď poloměr 20, nebo volbu průměru 40. + +## Vysunutí profilu +*Z 2D do výšky* + +- `_EXTRUDE` vezme uzavřenou křivku nebo oblast a táhne ji o výšku. +- Profil musí být zavřený. Čtyři nenapojené úsečky vysunout nejdou. +- Kladná výška jde ve směru Z. Záporná na opačnou stranu. +- `_PRESSPULL` umí přitáhnout uzavřenou oblast, na kterou kliknete, bez předchozího výběru. + +> Tip: obdélník 80 × 50 vysunutý o 12 je destička z druháku, teď jako těleso. + +## Profil nejdřív ve 2D +*Plocha* + +- Profil nakreslete v rovině, ze které chcete táhnout. Nejčastěji půdorys, Z ven z obrazovky. +- `_REGION` z uzavřené křivky udělá jednu plochu. Vysouvá se spolehlivěji než volné čáry. +- Po vysunutí profil smíte smazat, nebo ho nechte na hladině PROFIL a tu zmrazte. +- Jeden profil, jedna výška. Osazení vznikne až druhým tělesem a booleanem v další lekci. + +> Tip: otevřená křivka se vysune na plochu, ne na těleso. Pro součást potřebujete těleso. + +## Zadání: destička jako těleso +*Cvičení* + +- Profil 120 × 70, `_EXTRUDE` o 10 mm. To je tloušťka destičky. +- Díru zatím nekreslete. Ověřte `_MASSPROP`: objem má být 120 × 70 × 10 = 84 000 mm³. +- Přední pohled ukáže 120 × 10. Horní pohled 120 × 70. +- Uložte jako desticka-3d.dwg. + +> Tip: když objem vyjde dvojnásobek, výška je 20 místo 10. Kóta textu to nespraví, `_MASSPROP` čte model. + + +# Kvíz + +## Jaký poloměr má `_CYLINDER` o průměru 40? +- 40 +- [x] 20 +- 80 +- 10 +> Příkaz bez přepnutí bere poloměr. Průměr je volba, nebo napište polovinu. + +## Co `_EXTRUDE` potřebuje, aby vzniklo těleso? +- Otevřenou úsečku +- [x] Uzavřený profil +- Razítko +- Už hotový válec +> Neuzavřený profil vytvoří jen plochu. Těleso má mít objem. + +## Kam míří kladná výška vysunutí? +- Vždy doprava na obrazovce +- [x] Ve směru osy Z aktuálního UCS +- Do záporného X +- Do razítka +> Špatně natočený UCS vysune destičku „na bok“. Před příkazem zkontrolujte osy. + +## Objem destičky 120 × 70 × 10 mm je? +- 840 mm³ +- [x] 84 000 mm³ +- 200 mm³ +- 12 000 mm³ +> 120 × 70 = 8 400, krát 10 je 84 000. `_MASSPROP` má ukázat totéž. diff --git a/content/technicka-grafika/4-rocnik/03-rotace.en.md b/content/technicka-grafika/4-rocnik/03-rotace.en.md new file mode 100644 index 0000000..4f4c6b4 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/03-rotace.en.md @@ -0,0 +1,6 @@ +--- +nazev: Revolving a profile +popis: A part symmetric about an axis is the section spun around. +nazev_cviceni: Quiz: revolve +nadpis_kvizu: Pick the profile and the axis +--- diff --git a/content/technicka-grafika/4-rocnik/03-rotace.md b/content/technicka-grafika/4-rocnik/03-rotace.md new file mode 100644 index 0000000..9a77666 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/03-rotace.md @@ -0,0 +1,80 @@ +--- +id: tg-4-rotace +nazev: Rotace profilu +popis: Součást souměrná podle osy vznikne otočením řezu. +nazev_cviceni: Kvíz: rotace +nadpis_kvizu: Vyberte profil a osu +--- + +# Výklad + +## Kdy rotovat +*Místo skládání válců* + +- `_REVOLVE` otočí uzavřený profil kolem osy o zadaný úhel. +- 360 stupňů udělá plné rotační těleso. 180 udělá polovinu. +- Hodí se na hřídel, pouzdro, přírubu a každou součást souměrnou podle osy. +- Profil je polovina řezu. Osa rotace je osa součásti a profil ji smí jen lemovat, ne křížit. + +> Tip: pouzdro z třetího ročníku je jeden profil stěny otočený o 360 stupňů, ne válec minus válec. Oba postupy jsou správně, rotace je u čistě rotačního dílu kratší. + +## Profil +*Polovina* + +- Nakreslete jen polovinu řezu, vpravo od osy. Uzavřete ho. +- U pouzdra Ø60 / Ø36 dlouhého 80 je profil obdélník 12 × 80. Dvanáct je tloušťka stěny. +- Osa je svislá hrana díry. Profil se osy dotýká jen vnější? Ne: vnitřní hrana profilu je o poloměr díry od osy. +- Vnitřní hrana leží 18 mm od osy, vnější 30 mm. Šířka profilu je 12 mm. + +> Tip: 36 / 2 = 18 a 60 / 2 = 30. Profil je pás mezi těmito poloměry. + +## Osa a úhel +*360* + +- Osu vyberte dva body, nebo jednu úsečku nakreslenou na hladině OSA. +- Úhel 360. Menší úhel nechá výseč a na školní hřídeli bývá omyl. +- Profil nesmí osu křížit. Křížení AutoCAD odmítne, nebo udělá těleso, které se protíná samo. +- Po rotaci profil zmrazte. V řezu ho zase použijete jako kontrolu. + +> Tip: když vznikne plný válec bez díry, profil začínal na ose. Vnitřní hrana musí být 18 mm od osy, ne 0. + +## Zadání: pouzdro rotací +*Cvičení* + +- Osa je úsečka z 0,0 do 0,80. +- Profil: obdélník s rohy 18,0 a 30,80. +- `_REVOLVE` o 360 stupňů kolem této osy. +- `_MASSPROP` objem mezikruží: π × (30² − 18²) × 80. Má vyjít zhruba 144 764 mm³. + +> Tip: 900 − 324 = 576, krát π je asi 1 810, krát 80 je asi 144 800. Řád tisíců, ne desítek. + + +# Kvíz + +## Jaký úhel udělá plné rotační těleso? +- 90 +- 180 +- [x] 360 +- 45 +> Polovina otáčky je výseč. Uzavřené pouzdro potřebuje celých 360 stupňů. + +## Kde leží vnitřní hrana profilu pouzdra Ø36? +- Na ose +- [x] 18 mm od osy +- 36 mm od osy +- 60 mm od osy +> Poloměr díry je polovina průměru. Profil díru ohraničuje, nesmí začínat na ose, jinak díra zmizí. + +## Co se stane, když profil osu kříží? +- Vznikne správné pouzdro +- [x] AutoCAD rotaci odmítne, nebo těleso protne samo sebe +- Profil se jen posune +- Vznikne šrafa +> Osa smí být hranou profilu, ne smí jím procházet skrz. + +## Proč rotovat pouzdro místo skládání dvou válců? +- Jinak to nejde +- [x] U rotačního dílu stačí jeden profil +- Rotace umí i hranaté osazení bez profilu +- `_REVOLVE` funguje i v LT +> Oba postupy jsou správně. Rotace je kratší, když je součást souměrná podle osy. LT ji neumí. diff --git a/content/technicka-grafika/4-rocnik/04-boolean.en.md b/content/technicka-grafika/4-rocnik/04-boolean.en.md new file mode 100644 index 0000000..e6fc00f --- /dev/null +++ b/content/technicka-grafika/4-rocnik/04-boolean.en.md @@ -0,0 +1,6 @@ +--- +nazev: Union, subtract and intersect +popis: Solids add and subtract like sets. +nazev_cviceni: Quiz: booleans +nadpis_kvizu: Pick the order of the solids +--- diff --git a/content/technicka-grafika/4-rocnik/04-boolean.md b/content/technicka-grafika/4-rocnik/04-boolean.md new file mode 100644 index 0000000..b833fe4 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/04-boolean.md @@ -0,0 +1,80 @@ +--- +id: tg-4-boolean +nazev: Sjednocení, rozdíl a průnik +popis: Tělesa se sčítají a odečítají jako množiny. +nazev_cviceni: Kvíz: boolean +nadpis_kvizu: Vyberte pořadí těles +--- + +# Výklad + +## Sjednocení +*Jeden kus* + +- `_UNION` slepí tělesa, která se dotýkají nebo pronikají, v jeden objekt. +- Osazení na hřídeli jsou dva válce. Po sjednocení je to jedna součást. +- Tělesa, která se nedotýkají, sjednocení také spojí do jedné skupiny, ale výrobek to není. +- Než sjednotíte, zkontrolujte přesah. Mezera 0,01 mm nechá dva kusy. + +> Tip: `_INTERFERE` ukáže, jestli se dvě tělesa opravdu pronikají, nebo se jen zdají dotýkat. + +## Rozdíl +*Díra* + +- `_SUBTRACT` nejdřív vyberete těleso, které má zůstat, potvrdíte, a teprve pak těleso, které se odečte. +- Obrácené pořadí smaže součást a nechá díru jako váleček. To je nejčastější chyba. +- Díra Ø10 je válec Ø10 delší, než je tloušťka stěny, ať rozdíl prořízne celou stěnu. +- Válec díry po rozdílu zmizí. Nemusíte ho mazat ručně. + +> Tip: válec díry nechte o milimetr přečuhovat z obou stran. Jinak na stěně zůstane blanka. + +## Průnik +*Společný objem* + +- `_INTERSECT` nechá jen objem, který mají tělesa společný. Zbytek zmizí. +- Hodí se, když potřebujete tvar daný dvěma podmínkami, třeba válec seříznutý kvádrem. +- Na běžnou díru průnik není. Díra je rozdíl. +- Po průniku zůstanou obě původní tělesa pryč. Když je budete potřebovat, předem je zkopírujte. + +> Tip: sjednocení přidá, rozdíl ubere, průnik nechá jen překryv. U díry je to vždy rozdíl. + +## Zadání: destička s dírou +*Cvičení* + +- Destička `_BOX` 120 × 70 × 10. +- Válec Ø16, střed v 60,35, výška od -1 do 11, ať prochází skrz. +- `_SUBTRACT`: nejdřív destička, potom válec. +- `_MASSPROP` objem: 84 000 minus objem válce v tloušťce 10. Válec Ø16 a výška 10 má objem π × 8² × 10 ≈ 2 011. Výsledek je asi 81 989 mm³. + +> Tip: když zmizela destička a zbyl váleček, pořadí rozdílu bylo obrácené. Vraťte krok zpět. + + +# Kvíz + +## Jakým pořadím vznikne díra? +- Nejdřív válec, potom destička +- [x] Nejdřív destička, potom válec +- `_UNION` +- `_INTERSECT` +> První výběr zůstává. Druhý se odečte. Obrácené pořadí nechá jen váleček. + +## Proč má válec díry přečnívat z destičky? +- Aby šel okótovat +- [x] Aby ve stěně nezůstala blanka +- Přečnívat nesmí, rozdíl by selhal +- Aby vznikl závit +> Válec zarovnaný přesně s plochou někdy nechá tenkou slupku. Milimetr přesahu ji odstraní. + +## Který příkaz slepí dva dotýkající se válce hřídele? +- `_SUBTRACT` +- [x] `_UNION` +- `_INTERSECT` +- `_REVOLVE` +> Osazení je jeden kus. Sjednocení z dvou válců udělá jedno těleso. + +## Co nechá `_INTERSECT`? +- Součet objemů +- [x] Jen společný překryv +- Díru +- Původní tělesa vedle výsledku +> Zbytek obou těles zmizí. Před průnikem si je zkopírujte, pokud je ještě budete potřebovat. diff --git a/content/technicka-grafika/4-rocnik/05-rez-a-flatshot.en.md b/content/technicka-grafika/4-rocnik/05-rez-a-flatshot.en.md new file mode 100644 index 0000000..cd46853 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/05-rez-a-flatshot.en.md @@ -0,0 +1,6 @@ +--- +nazev: Sectioning a solid and Flatshot +popis: From the model back to lines for the drawing. +nazev_cviceni: Quiz: the model section +nadpis_kvizu: Decide what lands on the sheet +--- diff --git a/content/technicka-grafika/4-rocnik/05-rez-a-flatshot.md b/content/technicka-grafika/4-rocnik/05-rez-a-flatshot.md new file mode 100644 index 0000000..c8f2f53 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/05-rez-a-flatshot.md @@ -0,0 +1,80 @@ +--- +id: tg-4-rez-modelu +nazev: Řez tělesem a plochý snímek +popis: Z modelu zpátky čáry pro výkres. +nazev_cviceni: Kvíz: řez modelu +nadpis_kvizu: Vyberte, co vznikne na papíře +--- + +# Výklad + +## Řezná rovina +*Uvnitř tělesa* + +- `_SECTIONPLANE` položí rovinu řezu. Živé zobrazení ukáže řez, aniž by těleso rozřízlo natrvalo. +- `_SLICE` těleso rozřízne doopravdy. Polovinu můžete nechat, nebo smazat. +- Na školní výkres řežte živě nebo si před `_SLICE` těleso zkopírujte. Původní model budete ještě potřebovat. +- Řez osou pouzdra má ukázat stěnu 12 mm a díru Ø36, stejně jako ruční řez ve třetím ročníku. + +> Tip: rovina, která mine osu, ukáže jinou tloušťku stěny. U pouzdra veďte řez osou. + +## Plochý snímek +*Čáry z pohledu* + +- `_FLATSHOT` promítne aktuální pohled do čar v rovině XY. +- Viditelné hrany dá na jednu hladinu, skryté na druhou. Skryté pak přepněte na čárkované. +- Než příkaz spustíte, nastavte pohled: přední pro nárys, horní pro půdorys. +- Snímek je 2D. Dá se kótovat jako výkres z druháku. Model vedle něj nechte nedotčený. + +> Tip: plochý snímek pořízený v šikmém pohledu není nárys. Nejdřív srovnejte pohled, potom foťte. + +## Co snímek neudělá +*Kóty a šrafa* + +- Snímek neumí šrafu řezu ani kóty. Ty doplníte vy, na hladiny SRAFA a KOTA. +- Neumí ani razítko. To patří do rozvržení. +- Čáry snímku bývají na nových hladinách. Přesuňte je na OBRYS a SKRYTE, ať platí školní tloušťky. +- Když model potom změníte, snímek se sám neopraví. Po úpravě modelu ho vytvořte znovu. + +> Tip: výkresové pohledy z pozdější lekce se na rozdíl od snímku umí s modelem aktualizovat. Snímek je rychlá kontrola. + +## Zadání: nárys z tělesa +*Cvičení* + +- Otevřete pouzdro z rotace. Přední pohled, `_FLATSHOT`, viditelné hrany na OBRYS. +- Do řezu osou doplňte šrafu stěny ručně, nebo použijte živý řez a z něj čáry. +- Okótujte Ø60, Ø36 a délku 80. Čísla musí sedět s `_MASSPROP` a s poloměry profilu. +- Snímek nesmí zůstat šikmo. Nárys je v rovině, díra je obdélník 36 × 80 uvnitř obdélníku 60 × 80. + +> Tip: v nárysu řezu jsou dvě vodorovné čáry díry. Jejich vzdálenost je 36. + + +# Kvíz + +## Čím rozříznete těleso natrvalo? +- `_FLATSHOT` +- [x] `_SLICE` +- `_ORBIT` +- `_PLOT` +> Živá řezná rovina model nechá celý. `_SLICE` ho rozdělí. Předtím si nechte kopii. + +## Co musí předcházet plochému snímku nárysu? +- Tisk +- [x] Srovnaný přední pohled +- Rozbití tělesa +- Šrafa +> Snímek promítne to, co právě vidíte. Šikmý orbit není nárys. + +## Doplní `_FLATSHOT` kóty? +- Ano, z hladiny KOTA +- [x] Ne +- Jen průměry +- Jen když je měřítko 1:1 +> Snímek jsou čáry. Kóty, šrafu a razítko dodáte vy. + +## Jak daleko jsou v nárysu řezu pouzdra od sebe čáry díry Ø36? +- 18 +- 60 +- [x] 36 +- 12 +> V řezu je díra obdélník. Jeho výška je průměr, ne poloměr. diff --git a/content/technicka-grafika/4-rocnik/06-zobrazeni.en.md b/content/technicka-grafika/4-rocnik/06-zobrazeni.en.md new file mode 100644 index 0000000..3a7cfc8 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/06-zobrazeni.en.md @@ -0,0 +1,6 @@ +--- +nazev: Visual styles +popis: Wireframe, hidden lines and shading for checking the shape. +nazev_cviceni: Quiz: display +nadpis_kvizu: Pick a style for the task +--- diff --git a/content/technicka-grafika/4-rocnik/06-zobrazeni.md b/content/technicka-grafika/4-rocnik/06-zobrazeni.md new file mode 100644 index 0000000..687c0a4 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/06-zobrazeni.md @@ -0,0 +1,80 @@ +--- +id: tg-4-zobrazeni +nazev: Vizuální styly +popis: Drát, skryté hrany a stínování pro kontrolu tvaru. +nazev_cviceni: Kvíz: zobrazení +nadpis_kvizu: Vyberte styl podle úkolu +--- + +# Výklad + +## Drát a skryté hrany +*Kontrola* + +- Drátový model ukáže každou hranu. Hodí se, když saháte na zadní vrchol. +- Styl skrytých hran schová, co je za materiálem. Tvar se čte jako na fotografii bez stínu. +- `_VSCURRENT` styly přepíná. Na pásu karet je stejná nabídka u pohledu. +- Styl nemění těleso. Špatně odečtená díra zůstane špatně v každém stylu. + +> Tip: nejdřív drát, ať trefíte hranu, potom skryté hrany, ať zkontrolujete, že díra opravdu prochází. + +## Stínování +*Objem* + +- Konceptuální nebo stínovaný styl ukáže, jestli je díra průchozí, nebo je to jen namalovaná kružnice. +- Barva tělesa se bere z hladiny. OBRYS a těleso smí mít vlastní hladinu MODEL. +- Materiál a render na škole stačí náznak. Odevzdává se výkres, ne obrázek. +- Stínování zpomalí otáčení. Na slabším počítači se vraťte do drátu, než budete modelovat dál. + +> Tip: když v řezu vidíte dno díry, válec byl krátký. Rozdíl stěnu neprošel. + +## Řezy bez rozbití +*Pohled* + +- Živý řez v kombinaci se stínováním je nejrychlejší kontrola tloušťky stěny. +- Pohled uložte `_VIEW`, ať se k němu vrátíte. Název třeba REZ-AA. +- Izometrický stínovaný pohled smí přijít na výkres jako názorný doplněk, ne jako jediný obraz. +- Barvy na obrazovce pořád tiskněte černě. Styl zobrazení není tabulka per. + +> Tip: uložený pohled nese i styl. Když REZ-AA otevřete a je z něj drát, někdo styl přepsal. + +## Zadání: tři kontroly destičky +*Cvičení* + +- Destičku s dírou Ø16 zobrazte jako drát, jako skryté hrany a stínovaně. +- Ve stínovaném horním pohledu musí být skrz díru vidět. Když není, opakujte rozdíl. +- Uložte pohled HORNI a pohled PREDNI. +- Do výkresu tyto pohledy zatím nevkládejte. Jde o kontrolu modelu. + +> Tip: díra, která ve stínu má dno stejné barvy jako destička, je slepá. Výška válce byla menší než 10 mm. + + +# Kvíz + +## Který styl nechá vidět i zadní hrany? +- Stínovaný +- [x] Drátový +- Skryté hrany +- Monochrome.ctb +> Drát nekryje nic. Skryté hrany a stín zadní geometrii schovají. + +## Změní vizuální styl objem tělesa? +- Ano, stín ubere materiál +- [x] Ne +- Jen u rozdílu +- Jen při tisku +> Styl je způsob kresby na obrazovce. `_MASSPROP` zůstane stejný. + +## Co máte vidět skrz průchozí díru ve stínovaném pohledu? +- Dno stejné barvy +- [x] Prostor za destičkou +- Šrafu ANSI31 +- Razítko +> Dno znamená, že válec díry byl krátký a rozdíl stěnu neprošel. + +## Čím se na papír dostane černá čára, když je model na obrazovce modrý? +- Vizuálním stylem +- [x] Tabulkou per při tisku +- Smazáním hladiny +- `_ORBIT` +> Barva na monitoru je pracovní. monochrome.ctb z ní na PDF udělá černou. diff --git a/content/technicka-grafika/4-rocnik/07-vykres-z-modelu.en.md b/content/technicka-grafika/4-rocnik/07-vykres-z-modelu.en.md new file mode 100644 index 0000000..4ad88b7 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/07-vykres-z-modelu.en.md @@ -0,0 +1,6 @@ +--- +nazev: A drawing from the 3D model +popis: A front view and a section that update with the model. +nazev_cviceni: Quiz: model views +nadpis_kvizu: Tie the sheet to the solid +--- diff --git a/content/technicka-grafika/4-rocnik/07-vykres-z-modelu.md b/content/technicka-grafika/4-rocnik/07-vykres-z-modelu.md new file mode 100644 index 0000000..00b0c55 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/07-vykres-z-modelu.md @@ -0,0 +1,80 @@ +--- +id: tg-4-vykres-z-modelu +nazev: Výkres z 3D modelu +popis: Nárys a řez, které se s modelem aktualizují. +nazev_cviceni: Kvíz: pohledy z modelu +nadpis_kvizu: Propojte výkres s tělesem +--- + +# Výklad + +## Základní pohled +*Z tělesa* + +- V rozvržení příkaz základního pohledu vezme těleso a položí na papír nárys, půdorys nebo izometrii. +- Měřítko pohledu nastavíte jako u výřezu, třeba 1:1 nebo 1:2. +- Odvozené pohledy se položí nad, pod nebo vedle. Drží evropské promítání, když ho tak nastavíte. +- Pohled je svázaný s modelem. Změna tělesa a aktualizace pohledu přepíše čáry. + +> Tip: plochý snímek se sám neopraví. Pohled z modelu ano. Na maturitní výkres použijte pohled. + +## Řez pohledu +*A-A* + +- Z základního pohledu uděláte řez tažením řezné čáry. Písmena A doplní AutoCAD, nebo je přepište na A. +- Šrafu řezu nastavte na ANSI31 a školní měřítko. Hřídel v podélném řezu pořád nešrafujte, i když to AutoCAD nabídne. +- Skryté čáry v pohledu vypněte, pokud řez vnitřek ukázal. Výkres zůstane čistý. +- Kóty klikejte na pohled, ne do modelu naslepo. Berou rozměr z tělesa. + +> Tip: kóta napojená na pohled se po aktualizaci přepočítá. Kóta nakreslená vedle jako obyčejná čára ne. + +## Papír +*A3* + +- Rámeček a razítko jsou v rozvržení, měřítko 1:1 vůči papíru. +- Tisk je Layout, 1:1, monochrome.ctb, jako ve třetím ročníku. +- Izometrický pohled bez kót smí být třetí obraz. Nesmí být jediný. +- Než odevzdáte, aktualizujte pohledy. Starý pohled lže, když jste model mezitím změnili. + +> Tip: ve stromu nebo přes aktualizaci pohledu uvidíte, jestli je obraz aktuální. Nevěřte čáře, která přežila smazanou díru. + +## Zadání: pouzdro na A3 +*Cvičení* + +- Z 3D pouzdra vytvořte nárys jako řez A-A a půdorys nebo bokorys s roztečí, pokud na modelu díry jsou. +- Měřítko 1:1, formát A3. Kóty Ø60, Ø36 a 80. +- Změňte v modelu délku na 90, aktualizujte pohled a přesvědčte se, že kóta délky ukáže 90. +- Vraťte délku na 80, ať odevzdáte zadání, ne zkoušku. PDF pojmenujte pouzdro-z-modelu.pdf. + +> Tip: když po změně modelu kóta zůstane 80, není napojená na pohled. Smažte ji a okótujte znovu. + + +# Kvíz + +## Čím se pohled z modelu liší od `_FLATSHOT`? +- Ničím +- [x] Po změně tělesa se dá aktualizovat +- Umí jen izometrii +- Funguje v LT +> Snímek je jednorázová kopie čar. Pohled zůstává svázaný s tělesem. + +## Kam patří rámeček výkresu z modelu? +- Dovnitř tělesa +- [x] Do rozvržení +- Na hladinu OBRYS modelu +- Do definice bloku +> Papír má vlastní měřítko. Rámeček v modelu by se zmenšil s pohledem. + +## Co uděláte po změně délky pouzdra v modelu? +- Nic, kóty se nemění nikdy +- [x] Aktualizujete pohled +- Smažete razítko +- Přepnete na LT +> Neaktualizovaný pohled ukazuje starý tvar. Kóta napojená na něj se přepočítá spolu s ním. + +## Jaké měřítko má tisk tohoto rozvržení? +- 1:2, protože model je velký +- [x] 1:1 +- Podle `_MASSPROP` +- Fit +> Měřítko obrazu drží pohled. Tisk papíru je 1:1, jinak se měřítko zdvojí. diff --git a/content/technicka-grafika/4-rocnik/08-maturitni-zadani.en.md b/content/technicka-grafika/4-rocnik/08-maturitni-zadani.en.md new file mode 100644 index 0000000..5bfcb04 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/08-maturitni-zadani.en.md @@ -0,0 +1,6 @@ +--- +nazev: Exam assignment +popis: From a sketch through the model to a PDF that stands on its own. +nazev_cviceni: Quiz: the exam piece +nadpis_kvizu: Walk the whole drawing +--- diff --git a/content/technicka-grafika/4-rocnik/08-maturitni-zadani.md b/content/technicka-grafika/4-rocnik/08-maturitni-zadani.md new file mode 100644 index 0000000..19d47ea --- /dev/null +++ b/content/technicka-grafika/4-rocnik/08-maturitni-zadani.md @@ -0,0 +1,80 @@ +--- +id: tg-4-maturitni-zadani +nazev: Maturitní zadání +popis: Od náčrtu přes model po PDF, které obstojí samo. +nazev_cviceni: Kvíz: maturita +nadpis_kvizu: Projdete celý výkres +--- + +# Výklad + +## Součást +*Přírubové pouzdro* + +- Dutý válec Ø70 / Ø40, délka 60. Na jednom konci příruba Ø110 tlustá 12. +- Čtyři díry Ø11 na roztečné kružnici Ø90 v přírubě. Jsou průchozí skrz přírubu, ne skrz celé pouzdro. +- Vnitřní hrana díry Ø40 má zkosení 1 × 45° na straně příruby. +- Materiál ocel. Jednotky milimetry. Model 1:1. + +> Tip: tloušťka stěny válce je (70 − 40) / 2 = 15 mm. Příruba přesahuje válec o (110 − 70) / 2 = 20 mm. + +## Model +*Pořadí* + +- Profil řezu rotujte, nebo složte válec Ø70, válec příruby Ø110 výšky 12 a odečtěte díru Ø40. +- Čtyři díry Ø11 vzniknou polárním polem válce na poloměru 45 a rozdílem od příruby. +- Válec díry Ø11 musí být delší než 12 mm, ať je díra průchozí. Do dutiny Ø40 díra Ø11 nezasahuje, leží v materiálu příruby. +- `_MASSPROP` spočítejte orientačně a porovnejte s ručním odhadem. Řád objemu je statisíce mm³, ne tisíce. + +> Tip: rozteč Ø90 je poloměr 45. Díra se středem na poloměru 55 by už ležela mimo přírubu Ø110. + +## Výkres +*A3, 1:1* + +- Nárys je řez A-A osou. Půdorys ukáže čtyři díry. Izometrie bez kót je doplněk. +- Kóty: Ø110, Ø70, Ø40, tloušťka příruby 12, délka 60, 4× Ø11 na Ø90, zkosení 1 × 45°. +- Hladiny OBRYS, OSA, SRAFA, KOTA, RAM. Šrafa jen v řezu, jeden směr, díry prázdné. +- Razítko: Přírubové pouzdro, ocel, 1:1, jméno. Tisk A3, Layout, 1:1, monochrome.ctb. + +> Tip: evropské promítání. Půdorys pod nárysem. Čtyři díry se kótují jednou. + +## Než odevzdáte +*Kontrola* + +- Každá kóta se dá z modelu ověřit. Žádný text není přepsaný. +- Vypnutá hladina KOTA nechá čistý tvar. Vypnutá SRAFA nechá obrys řezu. +- PDF se jmenuje prirubove-pouzdro-A3.pdf a rámeček má 420 × 297 mm. +- Soubor dwg odevzdejte spolu s PDF. Bez modelu výkres nejde opravit. + +> Tip: otevřete PDF na celou stránku. Když je razítko useknuté nebo šrafa černá, tisk opakujte. Na obrazovce AutoCADu to vidět nebylo. + + +# Kvíz + +## Jaká je tloušťka stěny válce Ø70 s dírou Ø40? +- 30 mm +- [x] 15 mm +- 40 mm +- 12 mm +> Rozdíl průměrů je 30 mm na obě strany. Jedna stěna má 15 mm. 12 mm je tloušťka příruby, ne stěny. + +## Kam patří čtyři díry Ø11? +- Skrz celou délku 60 +- [x] Skrz přírubu tlustou 12, na Ø90 +- Do díry Ø40 +- Na osu jako závit M11 +> Leží v materiálu příruby mezi Ø70 a Ø110. Rozteč 90, poloměr 45. + +## Jaké měřítko má model i tisk tohoto výkresu? +- Model 1:2, tisk 1:2 +- [x] Model 1:1, tisk rozvržení 1:1 +- Model 2:1, tisk Fit +- Měřítko se do razítka nepíše +> Součást i papír jsou ve skutečných milimetrech. Měřítko pohledu je 1:1, protože se A3 vejde. + +## Co odevzdáváte spolu s PDF? +- Jen izometrii +- [x] I soubor dwg +- Rozbitý blok razítka +- Výkres bez kót, kóty jsou v PDF +> Z PDF už model neopravíte. DwG je práce, PDF je odevzdaný tisk. diff --git a/ios/Graduately/AppModel.swift b/ios/Graduately/AppModel.swift index 5d7d419..bfe0b40 100644 --- a/ios/Graduately/AppModel.swift +++ b/ios/Graduately/AppModel.swift @@ -234,6 +234,26 @@ final class AppModel: ObservableObject { refresh() } + func markTg(_ id: Int) { + progress.markTg(id) + refresh() + } + + func markTg2(_ id: Int) { + progress.markTg2(id) + refresh() + } + + func markTg3(_ id: Int) { + progress.markTg3(id) + refresh() + } + + func markTg4(_ id: Int) { + progress.markTg4(id) + refresh() + } + func markMat0(_ id: Int) { progress.markMat0(id) refresh() diff --git a/ios/Graduately/Data/Content.swift b/ios/Graduately/Data/Content.swift index 7480e01..4b1aa1e 100644 --- a/ios/Graduately/Data/Content.swift +++ b/ios/Graduately/Data/Content.swift @@ -59,6 +59,10 @@ final class Content { let fyz2: [J] let fyz3: [J] let fyz4: [J] + let tg: [J] + let tg2: [J] + let tg3: [J] + let tg4: [J] let mat0: [J] let mat: [J] let mat2: [J] @@ -125,6 +129,10 @@ final class Content { fyz2 = raw.arr("fyz2") fyz3 = raw.arr("fyz3") fyz4 = raw.arr("fyz4") + tg = raw.arr("tg") + tg2 = raw.arr("tg2") + tg3 = raw.arr("tg3") + tg4 = raw.arr("tg4") mat0 = raw.arr("mat0") mat = raw.arr("mat") mat2 = raw.arr("mat2") @@ -187,6 +195,10 @@ final class Content { func fyz2Lesson(_ id: Int) -> J? { fyz2.first { $0.int("id") == id } } func fyz3Lesson(_ id: Int) -> J? { fyz3.first { $0.int("id") == id } } func fyz4Lesson(_ id: Int) -> J? { fyz4.first { $0.int("id") == id } } + func tgLesson(_ id: Int) -> J? { tg.first { $0.int("id") == id } } + func tg2Lesson(_ id: Int) -> J? { tg2.first { $0.int("id") == id } } + func tg3Lesson(_ id: Int) -> J? { tg3.first { $0.int("id") == id } } + func tg4Lesson(_ id: Int) -> J? { tg4.first { $0.int("id") == id } } func mat0Lesson(_ id: Int) -> J? { mat0.first { $0.int("id") == id } } func matLesson(_ id: Int) -> J? { mat.first { $0.int("id") == id } } func mat2Lesson(_ id: Int) -> J? { mat2.first { $0.int("id") == id } } diff --git a/ios/Graduately/Data/ProgressStore.swift b/ios/Graduately/Data/ProgressStore.swift index ddbf220..c7ea326 100644 --- a/ios/Graduately/Data/ProgressStore.swift +++ b/ios/Graduately/Data/ProgressStore.swift @@ -94,6 +94,14 @@ final class ProgressStore { func markFyz3(_ id: Int) { mark("fyz3.\(id)") } func fyz4Done(_ id: Int) -> Bool { done("fyz4.\(id)") } func markFyz4(_ id: Int) { mark("fyz4.\(id)") } + func tgDone(_ id: Int) -> Bool { done("tg.\(id)") } + func markTg(_ id: Int) { mark("tg.\(id)") } + func tg2Done(_ id: Int) -> Bool { done("tg2.\(id)") } + func markTg2(_ id: Int) { mark("tg2.\(id)") } + func tg3Done(_ id: Int) -> Bool { done("tg3.\(id)") } + func markTg3(_ id: Int) { mark("tg3.\(id)") } + func tg4Done(_ id: Int) -> Bool { done("tg4.\(id)") } + func markTg4(_ id: Int) { mark("tg4.\(id)") } func mat0Done(_ id: Int) -> Bool { done("mat0.\(id)") } func markMat0(_ id: Int) { mark("mat0.\(id)") } func matDone(_ id: Int) -> Bool { done("mat.\(id)") } @@ -288,6 +296,38 @@ func summarize(_ content: Content, _ p: ProgressStore) -> ProgressSum { sum.doneUnits += 1 } } + for l in content.tg { + sum.totalEx += 1 + sum.openUnits += 1 + if p.tgDone(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.tg2 { + sum.totalEx += 1 + sum.openUnits += 1 + if p.tg2Done(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.tg3 { + sum.totalEx += 1 + sum.openUnits += 1 + if p.tg3Done(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.tg4 { + sum.totalEx += 1 + sum.openUnits += 1 + if p.tg4Done(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } for l in content.mat0 { sum.totalEx += 1 sum.openUnits += 1 diff --git a/ios/Graduately/Route.swift b/ios/Graduately/Route.swift index b6290a6..61a8c0b 100644 --- a/ios/Graduately/Route.swift +++ b/ios/Graduately/Route.swift @@ -111,6 +111,19 @@ enum Route: Hashable { case fyz4Map case fyz4Lesson(Int) case fyz4Ex(Int) + case tgYears + case tgMap + case tgLesson(Int) + case tgEx(Int) + case tg2Map + case tg2Lesson(Int) + case tg2Ex(Int) + case tg3Map + case tg3Lesson(Int) + case tg3Ex(Int) + case tg4Map + case tg4Lesson(Int) + case tg4Ex(Int) case czechMap case mluvnice case mluvEx(Int) @@ -224,6 +237,19 @@ enum Route: Hashable { case .fyz4Map: return "fyz4map" case .fyz4Lesson(let id): return "fyz4unit\(id)" case .fyz4Ex(let id): return "fyz4ex\(id)" + case .tgYears: return "tgyears" + case .tgMap: return "tgmap" + case .tgLesson(let id): return "tgunit\(id)" + case .tgEx(let id): return "tgex\(id)" + case .tg2Map: return "tg2map" + case .tg2Lesson(let id): return "tg2unit\(id)" + case .tg2Ex(let id): return "tg2ex\(id)" + case .tg3Map: return "tg3map" + case .tg3Lesson(let id): return "tg3unit\(id)" + case .tg3Ex(let id): return "tg3ex\(id)" + case .tg4Map: return "tg4map" + case .tg4Lesson(let id): return "tg4unit\(id)" + case .tg4Ex(let id): return "tg4ex\(id)" case .czechMap: return "czechmap" case .mluvnice: return "mluvnice" case .mluvEx(let n): return "mluve\(n)" @@ -448,6 +474,32 @@ enum Route: Hashable { return [.fyzYears, .fyz4Map, .fyz4Lesson(id)] case .fyz4Ex(let id): return [.fyzYears, .fyz4Map, .fyz4Lesson(id), .fyz4Ex(id)] + case .tgYears: + return [.tgYears] + case .tgMap: + return [.tgYears, .tgMap] + case .tgLesson(let id): + return [.tgYears, .tgMap, .tgLesson(id)] + case .tgEx(let id): + return [.tgYears, .tgMap, .tgLesson(id), .tgEx(id)] + case .tg2Map: + return [.tgYears, .tg2Map] + case .tg2Lesson(let id): + return [.tgYears, .tg2Map, .tg2Lesson(id)] + case .tg2Ex(let id): + return [.tgYears, .tg2Map, .tg2Lesson(id), .tg2Ex(id)] + case .tg3Map: + return [.tgYears, .tg3Map] + case .tg3Lesson(let id): + return [.tgYears, .tg3Map, .tg3Lesson(id)] + case .tg3Ex(let id): + return [.tgYears, .tg3Map, .tg3Lesson(id), .tg3Ex(id)] + case .tg4Map: + return [.tgYears, .tg4Map] + case .tg4Lesson(let id): + return [.tgYears, .tg4Map, .tg4Lesson(id)] + case .tg4Ex(let id): + return [.tgYears, .tg4Map, .tg4Lesson(id), .tg4Ex(id)] case .czechMap: return [.czechMap] case .mluvnice: @@ -508,6 +560,11 @@ func routeFromPage(_ page: String) -> Route? { case "fyz2map": return .fyz2Map case "fyz3map": return .fyz3Map case "fyz4map": return .fyz4Map + case "tgyears": return .tgYears + case "tgmap": return .tgMap + case "tg2map": return .tg2Map + case "tg3map": return .tg3Map + case "tg4map": return .tg4Map case "czechmap": return .czechMap case "mluvnice": return .mluvnice case "readinglist": return .readingList @@ -617,6 +674,30 @@ func routeFromPage(_ page: String) -> Route? { if let m = page.wholeMatch(of: /fyzex(\d+)/), let n = Int(m.1) { return .fyzEx(n) } + if let m = page.wholeMatch(of: /tg4unit(\d+)/), let n = Int(m.1) { + return .tg4Lesson(n) + } + if let m = page.wholeMatch(of: /tg4ex(\d+)/), let n = Int(m.1) { + return .tg4Ex(n) + } + if let m = page.wholeMatch(of: /tg3unit(\d+)/), let n = Int(m.1) { + return .tg3Lesson(n) + } + if let m = page.wholeMatch(of: /tg3ex(\d+)/), let n = Int(m.1) { + return .tg3Ex(n) + } + if let m = page.wholeMatch(of: /tg2unit(\d+)/), let n = Int(m.1) { + return .tg2Lesson(n) + } + if let m = page.wholeMatch(of: /tg2ex(\d+)/), let n = Int(m.1) { + return .tg2Ex(n) + } + if let m = page.wholeMatch(of: /tgunit(\d+)/), let n = Int(m.1) { + return .tgLesson(n) + } + if let m = page.wholeMatch(of: /tgex(\d+)/), let n = Int(m.1) { + return .tgEx(n) + } if let m = page.wholeMatch(of: /lit4unit(\d+)/), let n = Int(m.1) { return .lit4Lesson(n) } diff --git a/ios/Graduately/UI/Components.swift b/ios/Graduately/UI/Components.swift index 5c74383..2765396 100644 --- a/ios/Graduately/UI/Components.swift +++ b/ios/Graduately/UI/Components.swift @@ -679,6 +679,7 @@ func subjectSymbol(_ icon: String) -> String { case "flask": return "flask.fill" case "bolt": return "bolt.fill" case "function": return "function" + case "ruler": return "ruler.fill" default: return "lock.fill" } } diff --git a/ios/Graduately/UI/ExercisesMore.swift b/ios/Graduately/UI/ExercisesMore.swift index 4987c01..53a8dc1 100644 --- a/ios/Graduately/UI/ExercisesMore.swift +++ b/ios/Graduately/UI/ExercisesMore.swift @@ -952,6 +952,39 @@ struct ChoiceLessonScreen: View { } } +struct TgQuizScreen: View { + @ObservedObject var vm: AppModel + let year: Int + let id: Int + + var body: some View { + let lesson: J? = { + switch year { + case 2: return vm.content.tg2Lesson(id) + case 3: return vm.content.tg3Lesson(id) + case 4: return vm.content.tg4Lesson(id) + default: return vm.content.tgLesson(id) + } + }() + if let lesson { + let spec = J([ + "type": "choice", + "title": vm.tr(lesson.str("exTitleKey")), + "sub": lesson.str("quizHeadKey"), + "questions": lesson.rawArray("quiz"), + ]) + DispatchExercise(vm: vm, spec: spec, titleFallback: vm.tr(lesson.str("exTitleKey"))) { + switch year { + case 2: vm.markTg2(id) + case 3: vm.markTg3(id) + case 4: vm.markTg4(id) + default: vm.markTg(id) + } + } + } + } +} + struct FyzQuizScreen: View { @ObservedObject var vm: AppModel let year: Int diff --git a/ios/Graduately/UI/Screens.swift b/ios/Graduately/UI/Screens.swift index 3113582..044065b 100644 --- a/ios/Graduately/UI/Screens.swift +++ b/ios/Graduately/UI/Screens.swift @@ -713,6 +713,12 @@ private func subjectSum(_ vm: AppModel, _ s: J) -> ProgressSum { + vm.content.mat2.filter { vm.progress.mat2Done($0.int("id")) }.count + vm.content.mat3.filter { vm.progress.mat3Done($0.int("id")) }.count + vm.content.mat4.filter { vm.progress.mat4Done($0.int("id")) }.count + case "tgyears", "tgmap", "tg2map", "tg3map", "tg4map": + sum.totalEx = vm.content.tg.count + vm.content.tg2.count + vm.content.tg3.count + vm.content.tg4.count + sum.doneEx = vm.content.tg.filter { vm.progress.tgDone($0.int("id")) }.count + + vm.content.tg2.filter { vm.progress.tg2Done($0.int("id")) }.count + + vm.content.tg3.filter { vm.progress.tg3Done($0.int("id")) }.count + + vm.content.tg4.filter { vm.progress.tg4Done($0.int("id")) }.count case "fyzyears", "fyzmap", "fyz2map", "fyz3map", "fyz4map": sum.totalEx = vm.content.fyz.count + vm.content.fyz2.count + vm.content.fyz3.count + vm.content.fyz4.count sum.doneEx = vm.content.fyz.filter { vm.progress.fyzDone($0.int("id")) }.count @@ -1015,6 +1021,27 @@ private func buildSearch(_ vm: AppModel) -> [Hit] { add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "fyz4unit\(l.int("id"))", extra: "fyzika") add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "fyz4ex\(l.int("id"))") } + add(vm.tr("Technická grafika"), vm.tr("search_lesson"), "tgyears", extra: "autocad grafika") + add(vm.tr("tg_year1"), vm.tr("search_lesson"), "tgmap", extra: "rocnik") + add(vm.tr("tg_year2"), vm.tr("search_lesson"), "tg2map", extra: "rocnik") + add(vm.tr("tg_year3"), vm.tr("search_lesson"), "tg3map", extra: "rocnik") + add(vm.tr("tg_year4"), vm.tr("search_lesson"), "tg4map", extra: "rocnik") + for l in vm.content.tg { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "tgunit\(l.int("id"))", extra: "autocad") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tgex\(l.int("id"))") + } + for l in vm.content.tg2 { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "tg2unit\(l.int("id"))", extra: "autocad") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tg2ex\(l.int("id"))") + } + for l in vm.content.tg3 { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "tg3unit\(l.int("id"))", extra: "autocad") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tg3ex\(l.int("id"))") + } + for l in vm.content.tg4 { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "tg4unit\(l.int("id"))", extra: "autocad") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tg4ex\(l.int("id"))") + } add(vm.tr("Chemie"), vm.tr("search_lesson"), "chemmap", extra: "chemie") add(vm.tr("Biologie"), vm.tr("search_lesson"), "biomap", extra: "biologie") for l in vm.content.chem { @@ -1749,6 +1776,47 @@ struct MatYearsScreen: View { } } +struct TgYearsScreen: View { + @ObservedObject var vm: AppModel + + var body: some View { + #if os(macOS) + let years: [(String, String, Route)] = [ + ("tg_year1", "tg_sub", .tgMap), + ("tg_year2", "tg2_sub", .tg2Map), + ("tg_year3", "tg3_sub", .tg3Map), + ("tg_year4", "tg4_sub", .tg4Map), + ] + MacFillGrid(count: years.count, minWidth: 260, minHeight: 180) { i in + NavigationLink(value: years[i].2) { + MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).") + } + .buttonStyle(PressScaleStyle()) + } + .navigationTitle(vm.tr("Technická grafika")) + .navigationSubtitle(vm.tr("tg_years_sub")) + #else + List { + NavigationLink(value: Route.tgMap) { + ListRowLabel(title: vm.tr("tg_year1"), subtitle: vm.tr("tg_sub")) + } + NavigationLink(value: Route.tg2Map) { + ListRowLabel(title: vm.tr("tg_year2"), subtitle: vm.tr("tg2_sub")) + } + NavigationLink(value: Route.tg3Map) { + ListRowLabel(title: vm.tr("tg_year3"), subtitle: vm.tr("tg3_sub")) + } + NavigationLink(value: Route.tg4Map) { + ListRowLabel(title: vm.tr("tg_year4"), subtitle: vm.tr("tg4_sub")) + } + } + .navigationTitle(vm.tr("Technická grafika")) + .navigationSubtitle(vm.tr("tg_years_sub")) + .appListStyle() + #endif + } +} + struct FyzYearsScreen: View { @ObservedObject var vm: AppModel @@ -2808,6 +2876,72 @@ struct RouteDestination: View { } case .fyz4Ex(let id): FyzQuizScreen(vm: vm, year: 4, id: id) + case .tgYears: + TgYearsScreen(vm: vm) + case .tgMap: + LessonListScreen( + vm: vm, + title: vm.tr("tg_year1"), + subtitle: vm.tr("tg_sub"), + rows: vm.content.tg.map { l in + let id = l.int("id") + return LessonRow(id: "t\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.tgDone(id), destination: .tgLesson(id)) + } + ) + case .tgLesson(let id): + if let l = vm.content.tgLesson(id) { + SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .tgEx(id)) + } + case .tgEx(let id): + TgQuizScreen(vm: vm, year: 1, id: id) + case .tg2Map: + LessonListScreen( + vm: vm, + title: vm.tr("tg_year2"), + subtitle: vm.tr("tg2_sub"), + rows: vm.content.tg2.map { l in + let id = l.int("id") + return LessonRow(id: "t2\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.tg2Done(id), destination: .tg2Lesson(id)) + } + ) + case .tg2Lesson(let id): + if let l = vm.content.tg2Lesson(id) { + SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .tg2Ex(id)) + } + case .tg2Ex(let id): + TgQuizScreen(vm: vm, year: 2, id: id) + case .tg3Map: + LessonListScreen( + vm: vm, + title: vm.tr("tg_year3"), + subtitle: vm.tr("tg3_sub"), + rows: vm.content.tg3.map { l in + let id = l.int("id") + return LessonRow(id: "t3\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.tg3Done(id), destination: .tg3Lesson(id)) + } + ) + case .tg3Lesson(let id): + if let l = vm.content.tg3Lesson(id) { + SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .tg3Ex(id)) + } + case .tg3Ex(let id): + TgQuizScreen(vm: vm, year: 3, id: id) + case .tg4Map: + LessonListScreen( + vm: vm, + title: vm.tr("tg_year4"), + subtitle: vm.tr("tg4_sub"), + rows: vm.content.tg4.map { l in + let id = l.int("id") + return LessonRow(id: "t4\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.tg4Done(id), destination: .tg4Lesson(id)) + } + ) + case .tg4Lesson(let id): + if let l = vm.content.tg4Lesson(id) { + SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .tg4Ex(id)) + } + case .tg4Ex(let id): + TgQuizScreen(vm: vm, year: 4, id: id) case .mluvnice: LessonListScreen( vm: vm, diff --git a/scripts/build-content.py b/scripts/build-content.py index fe5fbcf..d6685cb 100644 --- a/scripts/build-content.py +++ b/scripts/build-content.py @@ -1065,6 +1065,8 @@ STD = [ ("prirodni-vedy/chemie", "chem"), ("prirodni-vedy/biologie", "bio"), ("fyzika/1-rocnik", "fyz"), ("fyzika/2-rocnik", "fyz2"), ("fyzika/3-rocnik", "fyz3"), ("fyzika/4-rocnik", "fyz4"), + ("technicka-grafika/1-rocnik", "tg"), ("technicka-grafika/2-rocnik", "tg2"), + ("technicka-grafika/3-rocnik", "tg3"), ("technicka-grafika/4-rocnik", "tg4"), ] MATH = [ ("matematika/0-opakovani", "mat0"), ("matematika/1-rocnik", "mat"), From 333085ee5370e648151bf0b90c89f205fb8a2afa Mon Sep 17 00:00:00 2001 From: pepaskopec-blip Date: Sun, 11 Oct 2026 14:37:14 +0200 Subject: [PATCH 3/7] Add a C# course with a side-by-side code runner. Each lesson explains a topic and runs the edited program next to the text through the local .NET SDK. Co-authored-by: Cursor --- README.md | 8 +- .../maturita/maturita/desktop/CSharpLab.kt | 128 ++++++ .../maturita/desktop/DesktopPlatform.kt | 9 + .../shared/src/org/maturita/maturita/App.kt | 47 +++ .../src/org/maturita/maturita/AppViewModel.kt | 20 + .../shared/src/org/maturita/maturita/Route.kt | 35 ++ .../src/org/maturita/maturita/data/Content.kt | 8 + .../maturita/maturita/data/ProgressStore.kt | 40 ++ .../maturita/maturita/platform/Platform.kt | 10 + .../org/maturita/maturita/ui/CodeLesson.kt | 158 ++++++++ .../src/org/maturita/maturita/ui/Screens.kt | 40 ++ content/README.md | 2 +- content/aplikace/novinky.txt | 6 + content/aplikace/predmety.md | 2 +- content/aplikace/texty.md | 23 ++ .../programovani/1-rocnik/01-ahoj-svete.en.md | 4 + .../programovani/1-rocnik/01-ahoj-svete.md | 42 ++ .../programovani/1-rocnik/02-komentare.en.md | 4 + content/programovani/1-rocnik/02-komentare.md | 35 ++ .../programovani/1-rocnik/03-promenne.en.md | 4 + content/programovani/1-rocnik/03-promenne.md | 36 ++ content/programovani/1-rocnik/04-typy.en.md | 4 + content/programovani/1-rocnik/04-typy.md | 40 ++ .../programovani/1-rocnik/05-prevody.en.md | 4 + content/programovani/1-rocnik/05-prevody.md | 36 ++ .../programovani/1-rocnik/06-operatory.en.md | 4 + content/programovani/1-rocnik/06-operatory.md | 37 ++ .../programovani/1-rocnik/07-retezce.en.md | 4 + content/programovani/1-rocnik/07-retezce.md | 36 ++ content/programovani/1-rocnik/08-vstup.en.md | 4 + content/programovani/1-rocnik/08-vstup.md | 34 ++ content/programovani/2-rocnik/01-if.en.md | 4 + content/programovani/2-rocnik/01-if.md | 43 ++ content/programovani/2-rocnik/02-switch.en.md | 4 + content/programovani/2-rocnik/02-switch.md | 43 ++ content/programovani/2-rocnik/03-while.en.md | 4 + content/programovani/2-rocnik/03-while.md | 38 ++ content/programovani/2-rocnik/04-for.en.md | 4 + content/programovani/2-rocnik/04-for.md | 34 ++ content/programovani/2-rocnik/05-pole.en.md | 4 + content/programovani/2-rocnik/05-pole.md | 35 ++ .../programovani/2-rocnik/06-foreach.en.md | 4 + content/programovani/2-rocnik/06-foreach.md | 35 ++ content/programovani/2-rocnik/07-metody.en.md | 4 + content/programovani/2-rocnik/07-metody.md | 37 ++ content/programovani/2-rocnik/08-navrat.en.md | 4 + content/programovani/2-rocnik/08-navrat.md | 38 ++ content/programovani/3-rocnik/01-trida.en.md | 4 + content/programovani/3-rocnik/01-trida.md | 40 ++ .../3-rocnik/02-konstruktor.en.md | 4 + .../programovani/3-rocnik/02-konstruktor.md | 44 +++ .../programovani/3-rocnik/03-vlastnosti.en.md | 4 + .../programovani/3-rocnik/03-vlastnosti.md | 50 +++ .../3-rocnik/04-zapouzdreni.en.md | 4 + .../programovani/3-rocnik/04-zapouzdreni.md | 49 +++ .../programovani/3-rocnik/05-dedicnost.en.md | 4 + content/programovani/3-rocnik/05-dedicnost.md | 52 +++ .../3-rocnik/06-polymorfismus.en.md | 4 + .../programovani/3-rocnik/06-polymorfismus.md | 50 +++ .../programovani/3-rocnik/07-rozhrani.en.md | 4 + content/programovani/3-rocnik/07-rozhrani.md | 50 +++ content/programovani/3-rocnik/08-enum.en.md | 4 + content/programovani/3-rocnik/08-enum.md | 50 +++ .../programovani/4-rocnik/01-vyjimky.en.md | 4 + content/programovani/4-rocnik/01-vyjimky.md | 43 ++ content/programovani/4-rocnik/02-list.en.md | 4 + content/programovani/4-rocnik/02-list.md | 39 ++ .../programovani/4-rocnik/03-slovnik.en.md | 4 + content/programovani/4-rocnik/03-slovnik.md | 39 ++ content/programovani/4-rocnik/04-linq.en.md | 4 + content/programovani/4-rocnik/04-linq.md | 36 ++ .../programovani/4-rocnik/05-soubory.en.md | 4 + content/programovani/4-rocnik/05-soubory.md | 33 ++ .../programovani/4-rocnik/06-generika.en.md | 4 + content/programovani/4-rocnik/06-generika.md | 37 ++ .../programovani/4-rocnik/07-delegaty.en.md | 4 + content/programovani/4-rocnik/07-delegaty.md | 34 ++ content/programovani/4-rocnik/08-async.en.md | 4 + content/programovani/4-rocnik/08-async.md | 33 ++ ios/Graduately/AppModel.swift | 20 + ios/Graduately/Data/Content.swift | 12 + ios/Graduately/Data/ProgressStore.swift | 40 ++ ios/Graduately/Route.swift | 53 +++ ios/Graduately/UI/Components.swift | 1 + ios/Graduately/UI/Screens.swift | 370 ++++++++++++++++++ scripts/build-content.py | 49 ++- 86 files changed, 2482 insertions(+), 5 deletions(-) create mode 100644 android/desktop/src/main/kotlin/org/maturita/maturita/desktop/CSharpLab.kt create mode 100644 android/shared/src/org/maturita/maturita/ui/CodeLesson.kt create mode 100644 content/programovani/1-rocnik/01-ahoj-svete.en.md create mode 100644 content/programovani/1-rocnik/01-ahoj-svete.md create mode 100644 content/programovani/1-rocnik/02-komentare.en.md create mode 100644 content/programovani/1-rocnik/02-komentare.md create mode 100644 content/programovani/1-rocnik/03-promenne.en.md create mode 100644 content/programovani/1-rocnik/03-promenne.md create mode 100644 content/programovani/1-rocnik/04-typy.en.md create mode 100644 content/programovani/1-rocnik/04-typy.md create mode 100644 content/programovani/1-rocnik/05-prevody.en.md create mode 100644 content/programovani/1-rocnik/05-prevody.md create mode 100644 content/programovani/1-rocnik/06-operatory.en.md create mode 100644 content/programovani/1-rocnik/06-operatory.md create mode 100644 content/programovani/1-rocnik/07-retezce.en.md create mode 100644 content/programovani/1-rocnik/07-retezce.md create mode 100644 content/programovani/1-rocnik/08-vstup.en.md create mode 100644 content/programovani/1-rocnik/08-vstup.md create mode 100644 content/programovani/2-rocnik/01-if.en.md create mode 100644 content/programovani/2-rocnik/01-if.md create mode 100644 content/programovani/2-rocnik/02-switch.en.md create mode 100644 content/programovani/2-rocnik/02-switch.md create mode 100644 content/programovani/2-rocnik/03-while.en.md create mode 100644 content/programovani/2-rocnik/03-while.md create mode 100644 content/programovani/2-rocnik/04-for.en.md create mode 100644 content/programovani/2-rocnik/04-for.md create mode 100644 content/programovani/2-rocnik/05-pole.en.md create mode 100644 content/programovani/2-rocnik/05-pole.md create mode 100644 content/programovani/2-rocnik/06-foreach.en.md create mode 100644 content/programovani/2-rocnik/06-foreach.md create mode 100644 content/programovani/2-rocnik/07-metody.en.md create mode 100644 content/programovani/2-rocnik/07-metody.md create mode 100644 content/programovani/2-rocnik/08-navrat.en.md create mode 100644 content/programovani/2-rocnik/08-navrat.md create mode 100644 content/programovani/3-rocnik/01-trida.en.md create mode 100644 content/programovani/3-rocnik/01-trida.md create mode 100644 content/programovani/3-rocnik/02-konstruktor.en.md create mode 100644 content/programovani/3-rocnik/02-konstruktor.md create mode 100644 content/programovani/3-rocnik/03-vlastnosti.en.md create mode 100644 content/programovani/3-rocnik/03-vlastnosti.md create mode 100644 content/programovani/3-rocnik/04-zapouzdreni.en.md create mode 100644 content/programovani/3-rocnik/04-zapouzdreni.md create mode 100644 content/programovani/3-rocnik/05-dedicnost.en.md create mode 100644 content/programovani/3-rocnik/05-dedicnost.md create mode 100644 content/programovani/3-rocnik/06-polymorfismus.en.md create mode 100644 content/programovani/3-rocnik/06-polymorfismus.md create mode 100644 content/programovani/3-rocnik/07-rozhrani.en.md create mode 100644 content/programovani/3-rocnik/07-rozhrani.md create mode 100644 content/programovani/3-rocnik/08-enum.en.md create mode 100644 content/programovani/3-rocnik/08-enum.md create mode 100644 content/programovani/4-rocnik/01-vyjimky.en.md create mode 100644 content/programovani/4-rocnik/01-vyjimky.md create mode 100644 content/programovani/4-rocnik/02-list.en.md create mode 100644 content/programovani/4-rocnik/02-list.md create mode 100644 content/programovani/4-rocnik/03-slovnik.en.md create mode 100644 content/programovani/4-rocnik/03-slovnik.md create mode 100644 content/programovani/4-rocnik/04-linq.en.md create mode 100644 content/programovani/4-rocnik/04-linq.md create mode 100644 content/programovani/4-rocnik/05-soubory.en.md create mode 100644 content/programovani/4-rocnik/05-soubory.md create mode 100644 content/programovani/4-rocnik/06-generika.en.md create mode 100644 content/programovani/4-rocnik/06-generika.md create mode 100644 content/programovani/4-rocnik/07-delegaty.en.md create mode 100644 content/programovani/4-rocnik/07-delegaty.md create mode 100644 content/programovani/4-rocnik/08-async.en.md create mode 100644 content/programovani/4-rocnik/08-async.md diff --git a/README.md b/README.md index db629d9..22b57c4 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,8 @@ Intel Mac, jiná architektura nebo úpravy kódu → [sestavení ze zdroje](#ses ročníky 2–4 po 8 lekcích (monitory, sestavení, RAID, servery, údržba) - **Technická grafika** — 4 ročníky AutoCADu, v každém 8 lekcí: výkres a souřadnice, úpravy a kóty, pohledy a tisk, 3D a maturitní zadání +- **Programování** — 4 ročníky C# po vzoru W3Schools. U každé lekce se kód + píše a spouští vedle výkladu, od výpisu po třídy, LINQ a async - **Český jazyk a literatura** — literatura (4 ročníky, 32 lekcí podle osnov středních škol), mluvnice (20 cvičení) a maturitní četba (82 knih: zápisky, kvíz, sestavení děje) @@ -92,7 +94,7 @@ Intel Mac, jiná architektura nebo úpravy kódu → [sestavení ze zdroje](#ses ### Použití -1. **Úvod** ukáže postup a mřížku předmětů. Otevřený předmět spustí cestu. Zamčené dlaždice zatím nic neotevřou: prezentační grafika, programování a programové vybavení. +1. **Úvod** ukáže postup a mřížku předmětů. Otevřený předmět spustí cestu. Zamčené dlaždice zatím nic neotevřou: prezentační grafika a programové vybavení. 2. U němčiny je učebnice (Neue Freunde, Aus aller Welt, Bei uns zu Hause) a čtyři ročníky. Ostatní otevřené předměty mají ročníky, lekce nebo knihy. 3. Z cesty se otevře výklad, poslech, čtení, psaní nebo kvíz. 4. **Statistiky**, **Nastavení** a **Hledat** jsou v postranním panelu na Macu. Na telefonu a v desktopové aplikaci jsou na záložkách, hledání navíc ikonou lupy. Zkratka je `Cmd/Ctrl+K`. @@ -263,6 +265,8 @@ Intel Mac, another architecture, or hacking on the code → years 2–4 have 8 lessons each (displays, assembly, RAID, servers, maintenance) - **Technical graphics** — 4 years of AutoCAD, 8 lessons each: the sheet and coordinates, editing and dimensions, views and plotting, 3D and the exam drawing +- **Programming** — 4 years of C# in the style of W3Schools. Each lesson has + code you edit and run beside the explanation, from output to classes, LINQ and async - **Czech language** — literature (4 years, 32 lessons from the secondary-school syllabus), grammar (20 exercises) and required reading (82 books: notes, quiz, plot ordering) @@ -283,7 +287,7 @@ Intel Mac, another architecture, or hacking on the code → ### Usage -1. **Welcome** shows progress and the subject grid. An open subject starts its path. Locked tiles do nothing yet: presentation graphics, programming and software. +1. **Welcome** shows progress and the subject grid. An open subject starts its path. Locked tiles do nothing yet: presentation graphics and software. 2. German has the textbook (Neue Freunde, Aus aller Welt, Bei uns zu Hause) and four years. The other open subjects have years, lessons or books. 3. A path opens an overview, listening, reading, writing or a quiz. 4. **Statistics**, **Settings** and **Search** sit in the sidebar on Mac. On the phone and the desktop app they are tabs, with search also on the magnifier. The shortcut is `Cmd/Ctrl+K`. diff --git a/android/desktop/src/main/kotlin/org/maturita/maturita/desktop/CSharpLab.kt b/android/desktop/src/main/kotlin/org/maturita/maturita/desktop/CSharpLab.kt new file mode 100644 index 0000000..ad7f5f1 --- /dev/null +++ b/android/desktop/src/main/kotlin/org/maturita/maturita/desktop/CSharpLab.kt @@ -0,0 +1,128 @@ +package org.maturita.maturita.desktop + +import org.maturita.maturita.platform.CSharpRun +import java.io.ByteArrayOutputStream +import java.io.File +import java.nio.charset.StandardCharsets +import java.util.concurrent.TimeUnit + +/** One cached console project. The student's program replaces Program.cs and `dotnet run` executes it. */ +object CSharpLab { + private val gate = Object() + + fun run(source: String, stdin: String): CSharpRun { + if (source.length > 100_000) return CSharpRun("fail", "Program je příliš dlouhý.") + val dotnet = findDotnet() ?: return CSharpRun("unavailable", "") + synchronized(gate) { + return try { + execute(dotnet, source, stdin) + } catch (e: Exception) { + CSharpRun("fail", e.message ?: "Spuštění selhalo.") + } + } + } + + private fun execute(dotnet: String, source: String, stdin: String): CSharpRun { + val dir = File(System.getProperty("user.home"), ".graduately/csharp-lab") + dir.mkdirs() + val csproj = File(dir, "Lab.csproj") + if (!csproj.isFile) { + csproj.writeText(projectFile(targetFramework(dotnet)), StandardCharsets.UTF_8) + val restored = command(dotnet, listOf("restore", dir.absolutePath), dir, "", 90) + if (restored.status != "ok") return restored + } + File(dir, "Program.cs").writeText(source, StandardCharsets.UTF_8) + val args = mutableListOf("run", "--project", dir.absolutePath, "--verbosity", "quiet") + if (File(dir, "obj").isDirectory) args += "--no-restore" + return command(dotnet, args, dir, stdin, 20) + } + + private fun command(dotnet: String, args: List, dir: File, stdin: String, seconds: Long): CSharpRun { + val process = ProcessBuilder(listOf(dotnet) + args) + .directory(dir) + .redirectErrorStream(true) + .apply { + environment().clear() + environment().putAll(environmentFor(dotnet)) + } + .start() + process.outputStream.use { it.write(stdin.toByteArray(StandardCharsets.UTF_8)) } + val output = ByteArrayOutputStream() + val reader = Thread { + process.inputStream.use { it.copyTo(output) } + } + reader.isDaemon = true + reader.start() + val finished = process.waitFor(seconds, TimeUnit.SECONDS) + if (!finished) { + process.descendants().forEach { it.destroyForcibly() } + process.destroyForcibly() + process.waitFor(2, TimeUnit.SECONDS) + reader.join(1000) + return CSharpRun("timeout", clip(output.toString(StandardCharsets.UTF_8))) + } + reader.join(2000) + val text = clip(output.toString(StandardCharsets.UTF_8)).trim() + return if (process.exitValue() == 0) CSharpRun("ok", text) else CSharpRun("fail", text) + } + + private fun clip(text: String): String = + if (text.length <= 16_000) text else text.take(16_000) + "\n…" + + private fun environmentFor(dotnet: String): Map { + val home = System.getProperty("user.home") + val root = File(dotnet).parent + val path = listOf(root, "/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin") + .joinToString(File.pathSeparator) + val env = mutableMapOf( + "PATH" to path, + "HOME" to home, + "USERPROFILE" to home, + "DOTNET_ROOT" to root, + "DOTNET_CLI_TELEMETRY_OPTOUT" to "1", + "DOTNET_NOLOGO" to "1", + "DOTNET_SKIP_FIRST_TIME_EXPERIENCE" to "1", + "TEMP" to System.getProperty("java.io.tmpdir"), + "TMP" to System.getProperty("java.io.tmpdir"), + ) + System.getenv("SystemRoot")?.let { env["SystemRoot"] = it } + System.getenv("ComSpec")?.let { env["ComSpec"] = it } + return env + } + + private fun targetFramework(dotnet: String): String { + val version = command(dotnet, listOf("--version"), File(System.getProperty("java.io.tmpdir")), "", 20) + val major = version.output.trim().substringBefore('.').filter { it.isDigit() } + return if (major.isEmpty()) "net8.0" else "net$major.0" + } + + private fun projectFile(framework: String) = """ + + + Exe + $framework + enable + disable + latest + + + """.trimIndent() + + private fun findDotnet(): String? { + val home = System.getProperty("user.home") + val programFiles = System.getenv("ProgramFiles") ?: "C:\\Program Files" + val names = listOf( + "/usr/local/share/dotnet/dotnet", + "/usr/share/dotnet/dotnet", + "/opt/homebrew/share/dotnet/dotnet", + "$home/.dotnet/dotnet", + "$programFiles\\dotnet\\dotnet.exe", + ) + names.firstOrNull { File(it).canExecute() }?.let { return it } + val path = System.getenv("PATH") ?: return null + return path.split(File.pathSeparator) + .map { File(it, if (System.getProperty("os.name").startsWith("Windows")) "dotnet.exe" else "dotnet") } + .firstOrNull { it.canExecute() } + ?.absolutePath + } +} diff --git a/android/desktop/src/main/kotlin/org/maturita/maturita/desktop/DesktopPlatform.kt b/android/desktop/src/main/kotlin/org/maturita/maturita/desktop/DesktopPlatform.kt index ced42c6..c61e57c 100644 --- a/android/desktop/src/main/kotlin/org/maturita/maturita/desktop/DesktopPlatform.kt +++ b/android/desktop/src/main/kotlin/org/maturita/maturita/desktop/DesktopPlatform.kt @@ -1,8 +1,10 @@ package org.maturita.maturita.desktop +import org.maturita.maturita.platform.CSharpRun import org.maturita.maturita.platform.Platform import org.maturita.maturita.platform.Prefs import org.maturita.maturita.platform.Speaker +import javax.swing.SwingUtilities class DesktopPlatform( override val commit: String, @@ -14,4 +16,11 @@ class DesktopPlatform( override val updater = DesktopUpdater(commit, install) override fun speaker(): Speaker? = DesktopSpeaker.create() + + override fun runCSharp(source: String, stdin: String, cb: (CSharpRun) -> Unit) { + Thread { + val result = CSharpLab.run(source, stdin) + SwingUtilities.invokeLater { cb(result) } + }.apply { isDaemon = true }.start() + } } diff --git a/android/shared/src/org/maturita/maturita/App.kt b/android/shared/src/org/maturita/maturita/App.kt index 4f2839a..df44197 100644 --- a/android/shared/src/org/maturita/maturita/App.kt +++ b/android/shared/src/org/maturita/maturita/App.kt @@ -23,6 +23,8 @@ import org.maturita.maturita.data.ColorMode import org.maturita.maturita.data.Palette import org.maturita.maturita.ui.BookListScreen import org.maturita.maturita.ui.BookScreen +import org.maturita.maturita.ui.CodeLessonScreen +import org.maturita.maturita.ui.CsYearsScreen import org.maturita.maturita.ui.CzechMapScreen import org.maturita.maturita.ui.GermanExercise import org.maturita.maturita.ui.HomeScreen @@ -584,6 +586,51 @@ private fun MaturitaAppBody(vm: AppViewModel) { } } is Route.Tg4Ex -> TgQuizScreen(vm, 4, r.id) + Route.CsYears -> CsYearsScreen(vm) + Route.CsMap -> LessonListScreen( + vm, vm.tr("cs_year1"), vm.tr("cs_sub"), + vm.content.cs.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.csDone(id), Route.CsLesson(id)) + }, + ) + is Route.CsLesson -> { + val l = vm.content.csLesson(r.id) + if (l != null) CodeLessonScreen(vm, l) { vm.markCs(r.id) } + } + Route.Cs2Map -> LessonListScreen( + vm, vm.tr("cs_year2"), vm.tr("cs2_sub"), + vm.content.cs2.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.cs2Done(id), Route.Cs2Lesson(id)) + }, + ) + is Route.Cs2Lesson -> { + val l = vm.content.cs2Lesson(r.id) + if (l != null) CodeLessonScreen(vm, l) { vm.markCs2(r.id) } + } + Route.Cs3Map -> LessonListScreen( + vm, vm.tr("cs_year3"), vm.tr("cs3_sub"), + vm.content.cs3.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.cs3Done(id), Route.Cs3Lesson(id)) + }, + ) + is Route.Cs3Lesson -> { + val l = vm.content.cs3Lesson(r.id) + if (l != null) CodeLessonScreen(vm, l) { vm.markCs3(r.id) } + } + Route.Cs4Map -> LessonListScreen( + vm, vm.tr("cs_year4"), vm.tr("cs4_sub"), + vm.content.cs4.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.cs4Done(id), Route.Cs4Lesson(id)) + }, + ) + is Route.Cs4Lesson -> { + val l = vm.content.cs4Lesson(r.id) + if (l != null) CodeLessonScreen(vm, l) { vm.markCs4(r.id) } + } Route.SciMap -> SciMapScreen(vm) Route.ChemMap -> LessonListScreen( vm, vm.tr("Chemie"), vm.tr("chem_sub"), diff --git a/android/shared/src/org/maturita/maturita/AppViewModel.kt b/android/shared/src/org/maturita/maturita/AppViewModel.kt index 956f666..f12a718 100644 --- a/android/shared/src/org/maturita/maturita/AppViewModel.kt +++ b/android/shared/src/org/maturita/maturita/AppViewModel.kt @@ -262,6 +262,26 @@ class AppViewModel(val content: Content, val platform: Platform) { refresh() } + fun markCs(id: Int) { + progress.markCs(id) + refresh() + } + + fun markCs2(id: Int) { + progress.markCs2(id) + refresh() + } + + fun markCs3(id: Int) { + progress.markCs3(id) + refresh() + } + + fun markCs4(id: Int) { + progress.markCs4(id) + refresh() + } + fun markMluv(n: Int) { progress.markMluv(n) refresh() diff --git a/android/shared/src/org/maturita/maturita/Route.kt b/android/shared/src/org/maturita/maturita/Route.kt index 1e6cdf8..254a296 100644 --- a/android/shared/src/org/maturita/maturita/Route.kt +++ b/android/shared/src/org/maturita/maturita/Route.kt @@ -118,6 +118,15 @@ sealed class Route(val page: String) { data object Tg4Map : Route("tg4map") data class Tg4Lesson(val id: Int) : Route("tg4unit$id") data class Tg4Ex(val id: Int) : Route("tg4ex$id") + data object CsYears : Route("csyears") + data object CsMap : Route("csmap") + data class CsLesson(val id: Int) : Route("csunit$id") + data object Cs2Map : Route("cs2map") + data class Cs2Lesson(val id: Int) : Route("cs2unit$id") + data object Cs3Map : Route("cs3map") + data class Cs3Lesson(val id: Int) : Route("cs3unit$id") + data object Cs4Map : Route("cs4map") + data class Cs4Lesson(val id: Int) : Route("cs4unit$id") data object CzechMap : Route("czechmap") data object Mluvnice : Route("mluvnice") data class MluvEx(val n: Int) : Route("mluve$n") @@ -263,6 +272,15 @@ sealed class Route(val page: String) { Tg4Map -> listOf(Subjects, TgYears, Tg4Map) is Tg4Lesson -> listOf(Subjects, TgYears, Tg4Map, this) is Tg4Ex -> listOf(Subjects, TgYears, Tg4Map, Tg4Lesson(id), this) + CsYears -> listOf(Subjects, CsYears) + CsMap -> listOf(Subjects, CsYears, CsMap) + is CsLesson -> listOf(Subjects, CsYears, CsMap, this) + Cs2Map -> listOf(Subjects, CsYears, Cs2Map) + is Cs2Lesson -> listOf(Subjects, CsYears, Cs2Map, this) + Cs3Map -> listOf(Subjects, CsYears, Cs3Map) + is Cs3Lesson -> listOf(Subjects, CsYears, Cs3Map, this) + Cs4Map -> listOf(Subjects, CsYears, Cs4Map) + is Cs4Lesson -> listOf(Subjects, CsYears, Cs4Map, this) CzechMap -> listOf(Subjects, CzechMap) Mluvnice -> listOf(Subjects, CzechMap, Mluvnice) is MluvEx -> listOf(Subjects, CzechMap, Mluvnice, this) @@ -320,6 +338,11 @@ fun routeFromPage(page: String): Route? = when (page) { "tg2map" -> Route.Tg2Map "tg3map" -> Route.Tg3Map "tg4map" -> Route.Tg4Map + "csyears" -> Route.CsYears + "csmap" -> Route.CsMap + "cs2map" -> Route.Cs2Map + "cs3map" -> Route.Cs3Map + "cs4map" -> Route.Cs4Map "czechmap" -> Route.CzechMap "mluvnice" -> Route.Mluvnice "readinglist" -> Route.ReadingList @@ -441,6 +464,18 @@ fun routeFromPage(page: String): Route? = when (page) { Regex("""tgex(\d+)""").matchEntire(page)?.let { return Route.TgEx(it.groupValues[1].toInt()) } + Regex("""cs4unit(\d+)""").matchEntire(page)?.let { + return Route.Cs4Lesson(it.groupValues[1].toInt()) + } + Regex("""cs3unit(\d+)""").matchEntire(page)?.let { + return Route.Cs3Lesson(it.groupValues[1].toInt()) + } + Regex("""cs2unit(\d+)""").matchEntire(page)?.let { + return Route.Cs2Lesson(it.groupValues[1].toInt()) + } + Regex("""csunit(\d+)""").matchEntire(page)?.let { + return Route.CsLesson(it.groupValues[1].toInt()) + } Regex("""lit4unit(\d+)""").matchEntire(page)?.let { return Route.Lit4Lesson(it.groupValues[1].toInt()) } diff --git a/android/shared/src/org/maturita/maturita/data/Content.kt b/android/shared/src/org/maturita/maturita/data/Content.kt index 5a82ac7..f48e8ae 100644 --- a/android/shared/src/org/maturita/maturita/data/Content.kt +++ b/android/shared/src/org/maturita/maturita/data/Content.kt @@ -60,6 +60,10 @@ class Content(root: JSONObject) { val tg2 = raw.arr("tg2") val tg3 = raw.arr("tg3") val tg4 = raw.arr("tg4") + val cs = raw.arr("cs") + val cs2 = raw.arr("cs2") + val cs3 = raw.arr("cs3") + val cs4 = raw.arr("cs4") val mat0 = raw.arr("mat0") val mat = raw.arr("mat") val mat2 = raw.arr("mat2") @@ -116,6 +120,10 @@ class Content(root: JSONObject) { fun tg2Lesson(id: Int) = tg2.firstOrNull { it.int("id") == id } fun tg3Lesson(id: Int) = tg3.firstOrNull { it.int("id") == id } fun tg4Lesson(id: Int) = tg4.firstOrNull { it.int("id") == id } + fun csLesson(id: Int) = cs.firstOrNull { it.int("id") == id } + fun cs2Lesson(id: Int) = cs2.firstOrNull { it.int("id") == id } + fun cs3Lesson(id: Int) = cs3.firstOrNull { it.int("id") == id } + fun cs4Lesson(id: Int) = cs4.firstOrNull { it.int("id") == id } fun mat0Lesson(id: Int) = mat0.firstOrNull { it.int("id") == id } fun matLesson(id: Int) = mat.firstOrNull { it.int("id") == id } fun mat2Lesson(id: Int) = mat2.firstOrNull { it.int("id") == id } diff --git a/android/shared/src/org/maturita/maturita/data/ProgressStore.kt b/android/shared/src/org/maturita/maturita/data/ProgressStore.kt index 1c29a6f..df6c24a 100644 --- a/android/shared/src/org/maturita/maturita/data/ProgressStore.kt +++ b/android/shared/src/org/maturita/maturita/data/ProgressStore.kt @@ -99,6 +99,14 @@ class ProgressStore(private val prefs: Prefs, content: Content) { fun markTg3(id: Int) { mark("tg3.$id") } fun tg4Done(id: Int) = done("tg4.$id") fun markTg4(id: Int) { mark("tg4.$id") } + fun csDone(id: Int) = done("cs.$id") + fun markCs(id: Int) { mark("cs.$id") } + fun cs2Done(id: Int) = done("cs2.$id") + fun markCs2(id: Int) { mark("cs2.$id") } + fun cs3Done(id: Int) = done("cs3.$id") + fun markCs3(id: Int) { mark("cs3.$id") } + fun cs4Done(id: Int) = done("cs4.$id") + fun markCs4(id: Int) { mark("cs4.$id") } fun mat0Done(id: Int) = done("mat0.$id") fun markMat0(id: Int) { mark("mat0.$id") } fun matDone(id: Int) = done("mat.$id") @@ -320,6 +328,38 @@ fun summarize(content: Content, p: ProgressStore): ProgressSum { sum.doneUnits += 1 } } + content.cs.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.csDone(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.cs2.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.cs2Done(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.cs3.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.cs3Done(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.cs4.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.cs4Done(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } content.mat0.forEach { l -> sum.totalEx += 1 sum.openUnits += 1 diff --git a/android/shared/src/org/maturita/maturita/platform/Platform.kt b/android/shared/src/org/maturita/maturita/platform/Platform.kt index afd88fa..e275434 100644 --- a/android/shared/src/org/maturita/maturita/platform/Platform.kt +++ b/android/shared/src/org/maturita/maturita/platform/Platform.kt @@ -18,8 +18,18 @@ interface Platform { /** A speech engine for listening exercises, or null when there is none. */ fun speaker(): Speaker? + + /** + * Compiles and runs a C# program. [cb] runs on the UI thread. + * Status is ok, fail, timeout or unavailable. + */ + fun runCSharp(source: String, stdin: String, cb: (CSharpRun) -> Unit) { + cb(CSharpRun("unavailable", "")) + } } +data class CSharpRun(val status: String, val output: String) + /** The subset of Android's SharedPreferences the app uses. */ interface Prefs { fun getBoolean(key: String, fallback: Boolean): Boolean diff --git a/android/shared/src/org/maturita/maturita/ui/CodeLesson.kt b/android/shared/src/org/maturita/maturita/ui/CodeLesson.kt new file mode 100644 index 0000000..c51b2ce --- /dev/null +++ b/android/shared/src/org/maturita/maturita/ui/CodeLesson.kt @@ -0,0 +1,158 @@ +package org.maturita.maturita.ui + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.FilledTonalButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import org.maturita.maturita.AppViewModel +import org.maturita.maturita.data.J + +/** Explanation on one side, the program and its output on the other. */ +@Composable +fun CodeLessonScreen(vm: AppViewModel, lesson: J, onSuccess: () -> Unit) { + val starter = lesson.str("code") + var code by remember(lesson.str("key")) { mutableStateOf(starter) } + var stdin by remember(lesson.str("key")) { mutableStateOf("") } + var output by remember(lesson.str("key")) { mutableStateOf("") } + var running by remember(lesson.str("key")) { mutableStateOf(false) } + + fun run() { + if (running) return + running = true + output = vm.tr("cs_running") + vm.platform.runCSharp(code, stdin) { result -> + running = false + output = when (result.status) { + "unavailable" -> vm.tr("cs_unavailable") + "timeout" -> (result.output + "\n\n" + vm.tr("cs_timeout")).trim() + else -> result.output.ifEmpty { vm.tr("cs_empty") } + } + if (result.status == "ok") onSuccess() + } + } + + DetailScaffold(vm, vm.tr(lesson.str("titleKey")), vm.tr(lesson.str("subKey"))) { inner -> + BoxWithConstraints(Modifier.fillMaxSize().padding(inner).padding(12.dp)) { + if (maxWidth >= 840.dp) { + Row(Modifier.fillMaxSize(), horizontalArrangement = Arrangement.spacedBy(12.dp)) { + Explanation(vm, lesson, Modifier.weight(0.9f).fillMaxHeight()) + EditorColumn(vm, code, { code = it }, stdin, { stdin = it }, expand = true, Modifier.weight(1.15f).fillMaxHeight()) + OutputColumn(vm, output, running, { run() }, { code = starter }, expand = true, Modifier.weight(1f).fillMaxHeight()) + } + } else { + Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.spacedBy(12.dp)) { + Explanation(vm, lesson, Modifier.fillMaxWidth()) + EditorColumn(vm, code, { code = it }, stdin, { stdin = it }, expand = false, Modifier.fillMaxWidth()) + OutputColumn(vm, output, running, { run() }, { code = starter }, expand = false, Modifier.fillMaxWidth()) + } + } + } + } +} + +@Composable +private fun Explanation(vm: AppViewModel, lesson: J, modifier: Modifier) { + Column(modifier.verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.spacedBy(14.dp)) { + lesson.arr("slides").forEach { slide -> + Text(slide.str("kicker"), color = MaterialTheme.colorScheme.primary, style = MaterialTheme.typography.labelLarge) + Text(slide.str("title"), style = MaterialTheme.typography.titleLarge) + slide.strs("lines").forEach { line -> + Text("• $line", style = MaterialTheme.typography.bodyLarge) + } + slide.strOrNull("tip")?.let { tip -> + Text(tip, color = MaterialTheme.colorScheme.onSurfaceVariant, style = MaterialTheme.typography.bodyMedium) + } + } + } +} + +@Composable +private fun EditorColumn( + vm: AppViewModel, + code: String, + onCode: (String) -> Unit, + stdin: String, + onStdin: (String) -> Unit, + expand: Boolean, + modifier: Modifier, +) { + Column(modifier, verticalArrangement = Arrangement.spacedBy(8.dp)) { + Text(vm.tr("cs_code"), style = MaterialTheme.typography.titleMedium) + BasicTextField( + value = code, + onValueChange = onCode, + textStyle = codeStyle().copy(color = MaterialTheme.colorScheme.onSurface), + cursorBrush = SolidColor(MaterialTheme.colorScheme.primary), + modifier = Modifier + .then(if (expand) Modifier.weight(1f) else Modifier.height(240.dp)) + .fillMaxWidth() + .background(MaterialTheme.colorScheme.surfaceContainer) + .padding(12.dp) + .verticalScroll(rememberScrollState()), + ) + Text(vm.tr("cs_stdin"), style = MaterialTheme.typography.labelLarge, color = MaterialTheme.colorScheme.onSurfaceVariant) + BasicTextField( + value = stdin, + onValueChange = onStdin, + textStyle = codeStyle().copy(color = MaterialTheme.colorScheme.onSurface), + cursorBrush = SolidColor(MaterialTheme.colorScheme.primary), + modifier = Modifier.fillMaxWidth().background(MaterialTheme.colorScheme.surfaceContainer).padding(12.dp), + ) + } +} + +@Composable +private fun OutputColumn( + vm: AppViewModel, + output: String, + running: Boolean, + onRun: () -> Unit, + onReset: () -> Unit, + expand: Boolean, + modifier: Modifier, +) { + Column(modifier, verticalArrangement = Arrangement.spacedBy(8.dp)) { + Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + FilledTonalButton(onClick = onRun, enabled = !running) { Text(vm.tr("cs_run")) } + OutlinedButton(onClick = onReset, enabled = !running) { Text(vm.tr("cs_reset")) } + } + Text(vm.tr("cs_output"), style = MaterialTheme.typography.titleMedium) + Text( + output.ifEmpty { vm.tr("cs_empty") }, + style = codeStyle(), + color = MaterialTheme.colorScheme.onSurface, + modifier = Modifier + .then(if (expand) Modifier.weight(1f) else Modifier.height(180.dp)) + .fillMaxWidth() + .background(MaterialTheme.colorScheme.surfaceContainer) + .padding(12.dp) + .verticalScroll(rememberScrollState()), + ) + } +} + +private fun codeStyle() = TextStyle(fontFamily = FontFamily.Monospace, fontSize = 14.sp, lineHeight = 20.sp) diff --git a/android/shared/src/org/maturita/maturita/ui/Screens.kt b/android/shared/src/org/maturita/maturita/ui/Screens.kt index bb4be71..761a024 100644 --- a/android/shared/src/org/maturita/maturita/ui/Screens.kt +++ b/android/shared/src/org/maturita/maturita/ui/Screens.kt @@ -36,6 +36,7 @@ import androidx.compose.material.icons.filled.Groups import androidx.compose.material.icons.filled.Lock import androidx.compose.material.icons.filled.Quiz import androidx.compose.material.icons.filled.Science +import androidx.compose.material.icons.filled.Terminal import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.outlined.Lightbulb import androidx.compose.material3.Card @@ -227,6 +228,7 @@ private fun SubjectIcon(icon: String, open: Boolean) { "bolt" -> Icon(Icons.Filled.Bolt, null, tint = tint, modifier = Modifier.size(26.dp)) "function" -> Icon(Icons.Filled.Functions, null, tint = tint, modifier = Modifier.size(26.dp)) "ruler" -> Icon(Icons.Filled.Architecture, null, tint = tint, modifier = Modifier.size(26.dp)) + "code" -> Icon(Icons.Filled.Terminal, null, tint = tint, modifier = Modifier.size(26.dp)) else -> Icon(Icons.Filled.Lock, null, tint = tint) } } @@ -350,6 +352,13 @@ private fun subjectSum(vm: AppViewModel, s: J): ProgressSum { vm.content.fyz3.count { vm.progress.fyz3Done(it.int("id")) } + vm.content.fyz4.count { vm.progress.fyz4Done(it.int("id")) } } + "csyears", "csmap", "cs2map", "cs3map", "cs4map" -> { + sum.totalEx = vm.content.cs.size + vm.content.cs2.size + vm.content.cs3.size + vm.content.cs4.size + sum.doneEx = vm.content.cs.count { vm.progress.csDone(it.int("id")) } + + vm.content.cs2.count { vm.progress.cs2Done(it.int("id")) } + + vm.content.cs3.count { vm.progress.cs3Done(it.int("id")) } + + vm.content.cs4.count { vm.progress.cs4Done(it.int("id")) } + } "tgyears", "tgmap", "tg2map", "tg3map", "tg4map" -> { sum.totalEx = vm.content.tg.size + vm.content.tg2.size + vm.content.tg3.size + vm.content.tg4.size sum.doneEx = vm.content.tg.count { vm.progress.tgDone(it.int("id")) } + @@ -679,6 +688,23 @@ private fun buildSearch(vm: AppViewModel): List { add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "tg4unit${l.int("id")}", extra = "autocad") add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tg4ex${l.int("id")}") } + add(vm.tr("Programování"), vm.tr("search_lesson"), "csyears", extra = "csharp c#") + add(vm.tr("cs_year1"), vm.tr("search_lesson"), "csmap", extra = "rocnik") + add(vm.tr("cs_year2"), vm.tr("search_lesson"), "cs2map", extra = "rocnik") + add(vm.tr("cs_year3"), vm.tr("search_lesson"), "cs3map", extra = "rocnik") + add(vm.tr("cs_year4"), vm.tr("search_lesson"), "cs4map", extra = "rocnik") + vm.content.cs.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "csunit${l.int("id")}", extra = "csharp") + } + vm.content.cs2.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "cs2unit${l.int("id")}", extra = "csharp") + } + vm.content.cs3.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "cs3unit${l.int("id")}", extra = "csharp") + } + vm.content.cs4.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "cs4unit${l.int("id")}", extra = "csharp") + } add(vm.tr("Chemie"), vm.tr("search_lesson"), "chemmap", extra = "chemie") add(vm.tr("Biologie"), vm.tr("search_lesson"), "biomap", extra = "biologie") vm.content.chem.forEach { l -> @@ -878,6 +904,20 @@ fun MatYearsScreen(vm: AppViewModel) { } } +@Composable +fun CsYearsScreen(vm: AppViewModel) { + DetailScaffold(vm, vm.tr("Programování"), vm.tr("cs_years_sub")) { inner -> + LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { + item { + NavRow(title = vm.tr("cs_year1"), subtitle = vm.tr("cs_sub"), leading = { Text("1.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.CsMap) } + NavRow(title = vm.tr("cs_year2"), subtitle = vm.tr("cs2_sub"), leading = { Text("2.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Cs2Map) } + NavRow(title = vm.tr("cs_year3"), subtitle = vm.tr("cs3_sub"), leading = { Text("3.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Cs3Map) } + NavRow(title = vm.tr("cs_year4"), subtitle = vm.tr("cs4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Cs4Map) } + } + } + } +} + @Composable fun TgYearsScreen(vm: AppViewModel) { DetailScaffold(vm, vm.tr("Technická grafika"), vm.tr("tg_years_sub")) { inner -> diff --git a/content/README.md b/content/README.md index da047be..f61e3a9 100644 --- a/content/README.md +++ b/content/README.md @@ -18,7 +18,7 @@ Skript vypíše počet lekcí, nebo chyby s cestou a číslem řádku | Složka | Obsah | |---|---| -| `pocitacove-site/`, `technicke-vybaveni/`, `technicka-grafika/`, `obcanska-nauka/`, `fyzika/` | `N-rocnik/`, v nich lekce | +| `pocitacove-site/`, `technicke-vybaveni/`, `technicka-grafika/`, `programovani/`, `obcanska-nauka/`, `fyzika/` | `N-rocnik/`, v nich lekce | | `prirodni-vedy/chemie`, `prirodni-vedy/biologie` | lekce | | `cestina/literatura/N-rocnik` | lekce literatury | | `cestina/mluvnice` | cvičení z mluvnice | diff --git a/content/aplikace/novinky.txt b/content/aplikace/novinky.txt index fb941b4..827a841 100644 --- a/content/aplikace/novinky.txt +++ b/content/aplikace/novinky.txt @@ -5,6 +5,12 @@ # cs: bullet # en: bullet +--- 2026-10-11 +cs: Programování: čtyři ročníky C#, od výpisu po LINQ a async +cs: Kód se píše v lekci a hned vedle se spustí +en: Programming: four years of C#, from output to LINQ and async +en: You write the code in the lesson and run it right beside the text + --- 2026-10-11 cs: Technická grafika: čtyři ročníky AutoCADu, od výkresu po 3D cs: V každé lekci je postup, zadání s rozměry a kvíz diff --git a/content/aplikace/predmety.md b/content/aplikace/predmety.md index 9266e4e..9ecbd76 100644 --- a/content/aplikace/predmety.md +++ b/content/aplikace/predmety.md @@ -13,5 +13,5 @@ | Základy přírodních věd | Základy přírodních věd | Fundamentals of Natural Sciences | flask | scimap | ano | | Technická grafika | Technická grafika | Technical Graphics | ruler | tgyears | ano | | Prezentační grafika | Prezentační grafika | Presentation Graphics | lock | | ne | -| Programování | Programování | Programming | lock | | ne | +| Programování | Programování | Programming | code | csyears | ano | | Programové vybavení | Programové vybavení | Software | lock | | ne | diff --git a/content/aplikace/texty.md b/content/aplikace/texty.md index d445708..3668457 100644 --- a/content/aplikace/texty.md +++ b/content/aplikace/texty.md @@ -243,6 +243,29 @@ | tg3_sub | Pohledy, řezy, bloky a tisk. | Views, sections, blocks and plotting. | | tg4_sub | 3D model a výkres k maturitě. | A 3D model and the exam drawing. | +# Programování + +| Klíč | Česky | Anglicky | +|---|---|---| +| cs_years_sub | C# od prvního výpisu po pokročilejší nástroje. Kód se píše a spouští vedle výkladu. | C# from the first line of output to the later tools. You write the code and run it beside the lesson. | +| cs_year1 | 1. ročník | Year 1 | +| cs_year2 | 2. ročník | Year 2 | +| cs_year3 | 3. ročník | Year 3 | +| cs_year4 | 4. ročník | Year 4 | +| cs_sub | Výpis, proměnné, typy, operátory a řetězce. | Output, variables, types, operators and strings. | +| cs2_sub | Podmínky, cykly, pole a metody. | Conditions, loops, arrays and methods. | +| cs3_sub | Třídy, dědičnost, rozhraní a výčty. | Classes, inheritance, interfaces and enums. | +| cs4_sub | Výjimky, kolekce, LINQ, soubory a async. | Exceptions, collections, LINQ, files and async. | +| cs_code | Kód | Code | +| cs_output | Výstup | Output | +| cs_stdin | Vstup pro Console.ReadLine | Input for Console.ReadLine | +| cs_run | Spustit | Run | +| cs_reset | Obnovit ukázku | Reset example | +| cs_running | Překládám… | Compiling… | +| cs_empty | Zatím nic. Upravte kód a spusťte ho. | Nothing yet. Edit the code and run it. | +| cs_timeout | Program běžel příliš dlouho a byl zastaven. | The program ran too long and was stopped. | +| cs_unavailable | Tady C# nespustíte. Na počítači s nainstalovaným .NET se výstup objeví vedle kódu. | C# cannot run here. On a computer with .NET installed the output appears beside the code. | + # Angličtina a němčina | Klíč | Česky | Anglicky | diff --git a/content/programovani/1-rocnik/01-ahoj-svete.en.md b/content/programovani/1-rocnik/01-ahoj-svete.en.md new file mode 100644 index 0000000..ee64874 --- /dev/null +++ b/content/programovani/1-rocnik/01-ahoj-svete.en.md @@ -0,0 +1,4 @@ +--- +nazev: Hello, world +popis: The first program: writing to the console. +--- diff --git a/content/programovani/1-rocnik/01-ahoj-svete.md b/content/programovani/1-rocnik/01-ahoj-svete.md new file mode 100644 index 0000000..c150fe8 --- /dev/null +++ b/content/programovani/1-rocnik/01-ahoj-svete.md @@ -0,0 +1,42 @@ +--- +id: cs-1-ahoj-svete +nazev: Ahoj světe +popis: První program: výpis na konzoli. +--- + +# Výklad + +## Výpis +*WriteLine* + +- Console.WriteLine vypíše text a odřádkuje. +- Console.Write vypíše text a zůstane na stejném řádku. +- Text se píše do uvozovek. Středník ukončuje příkaz. + +> Tip: Bez středníku se program nepřeloží. + +## Krátký a dlouhý zápis +*Main* + +- Starší učebnice mají třídu Program a metodu Main. +- Nový projekt stejný program zapíše několika řádky bez třídy. +- Obojí spustí stejný výpis. V lekcích používáme krátký zápis. + +> Tip: Krátký zápis je pořád C#. Není to jiný jazyk. + +## Vyzkoušejte +*Editor* + +- Vpravo je kód. Upravte ho a stiskněte Spustit. +- Výstup se objeví hned vedle. +- Obnovit ukázku vrátí původní program. + +> Tip: Změňte text v uvozovkách a spusťte znovu. + +# Kód + +``` +Console.WriteLine("Ahoj světe"); +Console.Write("Toto je "); +Console.WriteLine("C#"); +``` diff --git a/content/programovani/1-rocnik/02-komentare.en.md b/content/programovani/1-rocnik/02-komentare.en.md new file mode 100644 index 0000000..c80ebee --- /dev/null +++ b/content/programovani/1-rocnik/02-komentare.en.md @@ -0,0 +1,4 @@ +--- +nazev: Comments +popis: Notes the program does not run. +--- diff --git a/content/programovani/1-rocnik/02-komentare.md b/content/programovani/1-rocnik/02-komentare.md new file mode 100644 index 0000000..a54c0b2 --- /dev/null +++ b/content/programovani/1-rocnik/02-komentare.md @@ -0,0 +1,35 @@ +--- +id: cs-1-komentare +nazev: Komentáře +popis: Poznámky, které se nespouštějí. +--- + +# Výklad + +## Jednořádkový +*//* + +- Vše za // na stejném řádku počítač přeskočí. +- Komentář vysvětluje, proč tam řádek je, ne co je vidět. +- Kód bez komentáře pořád běží stejně. + +> Tip: Komentář není výpis. Na konzoli se neobjeví. + +## Více řádků +*/* */* + +- Blok začíná /* a končí */. +- Hodí se, když chcete dočasně vypnout několik řádků. +- Zapomenuté */ rozbije zbytek souboru. + +> Tip: Nevnořujte blokové komentáře do sebe. + +# Kód + +``` +// Tohle se nespustí. +Console.WriteLine("Tento řádek ano"); +/* +Console.WriteLine("A tento ne"); +*/ +``` diff --git a/content/programovani/1-rocnik/03-promenne.en.md b/content/programovani/1-rocnik/03-promenne.en.md new file mode 100644 index 0000000..e302009 --- /dev/null +++ b/content/programovani/1-rocnik/03-promenne.en.md @@ -0,0 +1,4 @@ +--- +nazev: Variables +popis: A named value you can change. +--- diff --git a/content/programovani/1-rocnik/03-promenne.md b/content/programovani/1-rocnik/03-promenne.md new file mode 100644 index 0000000..c17316f --- /dev/null +++ b/content/programovani/1-rocnik/03-promenne.md @@ -0,0 +1,36 @@ +--- +id: cs-1-promenne +nazev: Proměnné +popis: Pojmenovaná hodnota, kterou lze změnit. +--- + +# Výklad + +## Deklarace +*typ jméno* + +- Nejdřív typ, pak jméno, pak hodnota: int vek = 17; +- Jméno začíná písmenem. Mezery v něm být nesmí. +- Do stejné proměnné lze později uložit novou hodnotu stejného typu. + +> Tip: int drží celé číslo. string drží text. + +## Čtení +*jméno* + +- Jméno bez uvozovek je proměnná. Uvozovky by z něj udělaly text. +- WriteLine umí vypsat proměnnou i text spojený plusem. +- Hodnota se dosadí až při běhu, ne při psaní. + +> Tip: Změňte vek na 18 a spusťte znovu. + +# Kód + +``` +string jmeno = "Anna"; +int vek = 17; +jmeno = "Eva"; +Console.WriteLine(jmeno); +Console.WriteLine(vek); +Console.WriteLine(jmeno + " má " + vek + " let"); +``` diff --git a/content/programovani/1-rocnik/04-typy.en.md b/content/programovani/1-rocnik/04-typy.en.md new file mode 100644 index 0000000..0973087 --- /dev/null +++ b/content/programovani/1-rocnik/04-typy.en.md @@ -0,0 +1,4 @@ +--- +nazev: Data types +popis: int, double, bool, char and string. +--- diff --git a/content/programovani/1-rocnik/04-typy.md b/content/programovani/1-rocnik/04-typy.md new file mode 100644 index 0000000..bda4cec --- /dev/null +++ b/content/programovani/1-rocnik/04-typy.md @@ -0,0 +1,40 @@ +--- +id: cs-1-typy +nazev: Datové typy +popis: int, double, bool, char a string. +--- + +# Výklad + +## Čísla +*int a double* + +- int je celé číslo: 42. +- double je číslo s desetinnou tečkou: 3.14. +- V kódu je desetinná tečka, ne čárka. + +> Tip: 3,14 by C# přečetl jako dva údaje oddělené čárkou. + +## Text a ano/ne +*string, char, bool* + +- string je text v uvozovkách. +- char je jeden znak v apostrofech: 'A'. +- bool je jen true nebo false. + +> Tip: Apostrof a uvozovky nejsou totéž. 'A' je znak, "A" je text. + +# Kód + +``` +int cele = 42; +double desetinne = 3.14; +bool prsi = false; +char pismeno = 'A'; +string text = "ahoj"; +Console.WriteLine(cele); +Console.WriteLine(desetinne); +Console.WriteLine(prsi); +Console.WriteLine(pismeno); +Console.WriteLine(text); +``` diff --git a/content/programovani/1-rocnik/05-prevody.en.md b/content/programovani/1-rocnik/05-prevody.en.md new file mode 100644 index 0000000..291c616 --- /dev/null +++ b/content/programovani/1-rocnik/05-prevody.en.md @@ -0,0 +1,4 @@ +--- +nazev: Type conversion +popis: From double to int and from text to a number. +--- diff --git a/content/programovani/1-rocnik/05-prevody.md b/content/programovani/1-rocnik/05-prevody.md new file mode 100644 index 0000000..3dd2e2a --- /dev/null +++ b/content/programovani/1-rocnik/05-prevody.md @@ -0,0 +1,36 @@ +--- +id: cs-1-prevody +nazev: Převody typů +popis: Z double na int a z textu na číslo. +--- + +# Výklad + +## Ořez desetin +*(int)* + +- (int)3.9 je 3. Desetinná část se odřízne, nezaokrouhlí. +- Opačně int do double ztrátu nemá: 3 se stane 3.0. +- Převod napište před hodnotu do závorky. + +> Tip: Když potřebujete zaokrouhlit, (int) na to není. + +## Text na číslo +*Parse* + +- int.Parse("20") udělá z textu číslo 20. +- Když text číslo není, program spadne. To později chytíme výjimkou. +- Číslo na text udělá ToString nebo zápis do řetězce. + +> Tip: Vstup z konzole je vždy text, i když uživatel napíše číslici. + +# Kód + +``` +double cena = 19.9; +int cele = (int)cena; +Console.WriteLine(cele); +string vstup = "20"; +int cislo = int.Parse(vstup); +Console.WriteLine(cislo + 1); +``` diff --git a/content/programovani/1-rocnik/06-operatory.en.md b/content/programovani/1-rocnik/06-operatory.en.md new file mode 100644 index 0000000..e4c6901 --- /dev/null +++ b/content/programovani/1-rocnik/06-operatory.en.md @@ -0,0 +1,4 @@ +--- +nazev: Operators +popis: Arithmetic, remainder, comparison and logic. +--- diff --git a/content/programovani/1-rocnik/06-operatory.md b/content/programovani/1-rocnik/06-operatory.md new file mode 100644 index 0000000..b7c962a --- /dev/null +++ b/content/programovani/1-rocnik/06-operatory.md @@ -0,0 +1,37 @@ +--- +id: cs-1-operatory +nazev: Operátory +popis: Počítání, zbytek, porovnání a a zároveň. +--- + +# Výklad + +## Počet +*+ - * / %* + +- Sčítání, odčítání a násobení fungují, jak čekáte. +- Dělení dvou int je celočíselné: 17 / 5 je 3. +- % je zbytek po dělení: 17 % 5 je 2. + +> Tip: Když chcete desetinný podíl, jedno číslo udělejte double: 17 / 5.0. + +## Porovnání +*true a false* + +- == je rovnost. = je uložení. Nejsou totéž. +- && znamená a zároveň. || znamená nebo. +- ! otočí bool: !true je false. + +> Tip: Jedno = v podmínce je častá chyba. Podmínka chce ==. + +# Kód + +``` +int a = 17; +int b = 5; +Console.WriteLine(a + b); +Console.WriteLine(a / b); +Console.WriteLine(a % b); +Console.WriteLine(a > b); +Console.WriteLine(a > 10 && b < 10); +``` diff --git a/content/programovani/1-rocnik/07-retezce.en.md b/content/programovani/1-rocnik/07-retezce.en.md new file mode 100644 index 0000000..7229ab2 --- /dev/null +++ b/content/programovani/1-rocnik/07-retezce.en.md @@ -0,0 +1,4 @@ +--- +nazev: Strings +popis: Joining text, length and a slice. +--- diff --git a/content/programovani/1-rocnik/07-retezce.md b/content/programovani/1-rocnik/07-retezce.md new file mode 100644 index 0000000..044c950 --- /dev/null +++ b/content/programovani/1-rocnik/07-retezce.md @@ -0,0 +1,36 @@ +--- +id: cs-1-retezce +nazev: Řetězce +popis: Skládání textu, délka a výřez. +--- + +# Výklad + +## Interpolace +*$* + +- $"Ahoj, {jmeno}" dosadí hodnotu do složených závorek. +- Je přehlednější než spojování plusem. +- Do závorek může i výpočet: {vek + 1}. + +> Tip: Bez dolaru před uvozovkami se {jmeno} vypíše doslova. + +## Metody textu +*Length* + +- Length je počet znaků. +- ToUpper vrátí velká písmena a původní text nezmění. +- Substring(0, 2) vezme dva znaky od začátku. + +> Tip: První znak má index 0, ne 1. + +# Kód + +``` +string jmeno = "Anna"; +int vek = 17; +Console.WriteLine($"Ahoj, {jmeno}. Za rok ti bude {vek + 1}."); +Console.WriteLine(jmeno.Length); +Console.WriteLine(jmeno.ToUpper()); +Console.WriteLine(jmeno.Substring(0, 2)); +``` diff --git a/content/programovani/1-rocnik/08-vstup.en.md b/content/programovani/1-rocnik/08-vstup.en.md new file mode 100644 index 0000000..aaa9f2d --- /dev/null +++ b/content/programovani/1-rocnik/08-vstup.en.md @@ -0,0 +1,4 @@ +--- +nazev: Console input +popis: Console.ReadLine reads a line. +--- diff --git a/content/programovani/1-rocnik/08-vstup.md b/content/programovani/1-rocnik/08-vstup.md new file mode 100644 index 0000000..ea4349c --- /dev/null +++ b/content/programovani/1-rocnik/08-vstup.md @@ -0,0 +1,34 @@ +--- +id: cs-1-vstup +nazev: Vstup z konzole +popis: Console.ReadLine přečte řádek. +--- + +# Výklad + +## ReadLine +*text* + +- ReadLine počká na řádek a vrátí ho jako string. +- V této lekci řádek napište do pole Vstup a pak Spustit. +- Každé další ReadLine vezme další řádek vstupu. + +> Tip: Když vstup necháte prázdný, ReadLine dostane konec a program může spadnout. + +## Číslo ze vstupu +*Parse* + +- ReadLine nikdy nevrací int. Vrací text. +- int.Parse z něj číslo udělá. +- Napište do vstupu třeba 17. + +> Tip: Zkuste ve vstupu Anna a uvidíte chybu. Parse čeká číslo. + +# Kód + +``` +Console.WriteLine("Kolik ti je?"); +string radek = Console.ReadLine(); +int vek = int.Parse(radek); +Console.WriteLine($"Za rok ti bude {vek + 1}."); +``` diff --git a/content/programovani/2-rocnik/01-if.en.md b/content/programovani/2-rocnik/01-if.en.md new file mode 100644 index 0000000..177d905 --- /dev/null +++ b/content/programovani/2-rocnik/01-if.en.md @@ -0,0 +1,4 @@ +--- +nazev: If and else +popis: A branch that runs only sometimes. +--- diff --git a/content/programovani/2-rocnik/01-if.md b/content/programovani/2-rocnik/01-if.md new file mode 100644 index 0000000..c235df4 --- /dev/null +++ b/content/programovani/2-rocnik/01-if.md @@ -0,0 +1,43 @@ +--- +id: cs-2-if +nazev: If a else +popis: Větev, která se spustí jen někdy. +--- + +# Výklad + +## Podmínka +*if* + +- if (vek >= 18) spustí blok ve složených závorkách, jen když je podmínka true. +- else je větev pro opačný případ. +- else if přidá další podmínku mezi ně. + +> Tip: Za if není středník. Středník by podmínku oddělil od bloku. + +## Porovnání +*==* + +- Čísla porovnávejte >, <, >=, <=, == a !=. +- Text se porovnává ==. "Anna" == "anna" je false. +- Do podmínky patří bool, ne přiřazení. + +> Tip: Změňte vek a sledujte, která věta se vypíše. + +# Kód + +``` +int vek = 17; +if (vek >= 18) +{ + Console.WriteLine("plnoletý"); +} +else if (vek >= 15) +{ + Console.WriteLine("téměř"); +} +else +{ + Console.WriteLine("ještě ne"); +} +``` diff --git a/content/programovani/2-rocnik/02-switch.en.md b/content/programovani/2-rocnik/02-switch.en.md new file mode 100644 index 0000000..54377bb --- /dev/null +++ b/content/programovani/2-rocnik/02-switch.en.md @@ -0,0 +1,4 @@ +--- +nazev: Switch +popis: Several branches for one value. +--- diff --git a/content/programovani/2-rocnik/02-switch.md b/content/programovani/2-rocnik/02-switch.md new file mode 100644 index 0000000..d46a17c --- /dev/null +++ b/content/programovani/2-rocnik/02-switch.md @@ -0,0 +1,43 @@ +--- +id: cs-2-switch +nazev: Switch +popis: Víc větví podle jedné hodnoty. +--- + +# Výklad + +## Větve +*case* + +- switch vybere case, který se rovná hodnotě. +- break ukončí větev. Bez něj se překlad v klasickém zápisu zastaví. +- default je větev, když nesedí žádný case. + +> Tip: Na známku 1 až 5 se switch hodí víc než řada else if. + +## Nový zápis +*šipka* + +- case 1 => "výborně" je krátká větev bez break. +- Výraz switch umí hodnotu rovnou vrátit. +- V lekci je klasický zápis, ať je vidět break. + +> Tip: Přidejte case 5 a spusťte se znamkou 5. + +# Kód + +``` +int znamka = 2; +switch (znamka) +{ + case 1: + Console.WriteLine("výborně"); + break; + case 2: + Console.WriteLine("chvalitebně"); + break; + default: + Console.WriteLine("jiná známka"); + break; +} +``` diff --git a/content/programovani/2-rocnik/03-while.en.md b/content/programovani/2-rocnik/03-while.en.md new file mode 100644 index 0000000..cc1f71b --- /dev/null +++ b/content/programovani/2-rocnik/03-while.en.md @@ -0,0 +1,4 @@ +--- +nazev: While +popis: Repeat while a condition holds. +--- diff --git a/content/programovani/2-rocnik/03-while.md b/content/programovani/2-rocnik/03-while.md new file mode 100644 index 0000000..37455ca --- /dev/null +++ b/content/programovani/2-rocnik/03-while.md @@ -0,0 +1,38 @@ +--- +id: cs-2-while +nazev: While +popis: Opakování, dokud podmínka platí. +--- + +# Výklad + +## Smyčka +*while* + +- Tělo se opakuje, dokud je podmínka true. +- Když podmínku nikdy nezměníte, smyčka neskončí. +- Aplikace dlouhý běh po chvíli zastaví. + +> Tip: i = i + 1 nebo i++ posune čítač. Bez toho se i nemění. + +## Součet +*1 až 4* + +- Začněte součtem 0 a přičítejte i. +- 1+2+3+4 je 10. +- Podmínka i <= 4 pustí i číslo 4 dovnitř. + +> Tip: Změňte 4 na 10. Součet 1 až 10 je 55. + +# Kód + +``` +int i = 1; +int soucet = 0; +while (i <= 4) +{ + soucet = soucet + i; + i++; +} +Console.WriteLine(soucet); +``` diff --git a/content/programovani/2-rocnik/04-for.en.md b/content/programovani/2-rocnik/04-for.en.md new file mode 100644 index 0000000..757f660 --- /dev/null +++ b/content/programovani/2-rocnik/04-for.en.md @@ -0,0 +1,4 @@ +--- +nazev: For +popis: A loop with its counter in one place. +--- diff --git a/content/programovani/2-rocnik/04-for.md b/content/programovani/2-rocnik/04-for.md new file mode 100644 index 0000000..2e6afac --- /dev/null +++ b/content/programovani/2-rocnik/04-for.md @@ -0,0 +1,34 @@ +--- +id: cs-2-for +nazev: For +popis: Smyčka s čítačem na jednom místě. +--- + +# Výklad + +## Tři části +*start; podmínka; krok* + +- for (int i = 0; i < 3; i++) má začátek, podmínku a krok. +- i < 3 pustí 0, 1 a 2. Trojku už ne. +- Krok i++ se provede až po těle. + +> Tip: Když potřebujete přesný počet opakování, for je přehlednější než while. + +## Násobilka +*výpis* + +- V těle můžete počítat i * 2. +- WriteLine v těle vypíše jeden řádek na každé opakování. +- Čítač nemusí začínat nulou. + +> Tip: Přepište podmínku na i <= 5 a sledujte další řádek. + +# Kód + +``` +for (int i = 1; i <= 3; i++) +{ + Console.WriteLine($"{i} krát 2 je {i * 2}"); +} +``` diff --git a/content/programovani/2-rocnik/05-pole.en.md b/content/programovani/2-rocnik/05-pole.en.md new file mode 100644 index 0000000..a4664a1 --- /dev/null +++ b/content/programovani/2-rocnik/05-pole.en.md @@ -0,0 +1,4 @@ +--- +nazev: Arrays +popis: Several values of one type under one name. +--- diff --git a/content/programovani/2-rocnik/05-pole.md b/content/programovani/2-rocnik/05-pole.md new file mode 100644 index 0000000..4653b09 --- /dev/null +++ b/content/programovani/2-rocnik/05-pole.md @@ -0,0 +1,35 @@ +--- +id: cs-2-pole +nazev: Pole +popis: Víc hodnot stejného typu pod jedním jménem. +--- + +# Výklad + +## Index +*od nuly* + +- new int[] { 2, 4, 6 } je pole tří čísel. +- První prvek je znamky[0], poslední znamky[2]. +- Length je počet prvků, ne index posledního. + +> Tip: znamky[3] u pole délky 3 spadne. Poslední index je 2. + +## Zápis +*prvku* + +- Do prvku se dá uložit nová hodnota: znamky[1] = 5. +- Ostatní prvky zůstanou. +- Pole má pevnou délku. Přidat čtvrtý prvek nejde, na to je později List. + +> Tip: Vyměňte první známku a spusťte znovu. + +# Kód + +``` +int[] znamky = { 1, 3, 2 }; +Console.WriteLine(znamky[0]); +Console.WriteLine(znamky.Length); +znamky[1] = 5; +Console.WriteLine(znamky[1]); +``` diff --git a/content/programovani/2-rocnik/06-foreach.en.md b/content/programovani/2-rocnik/06-foreach.en.md new file mode 100644 index 0000000..3a4186e --- /dev/null +++ b/content/programovani/2-rocnik/06-foreach.en.md @@ -0,0 +1,4 @@ +--- +nazev: Foreach +popis: Visit every item without an index. +--- diff --git a/content/programovani/2-rocnik/06-foreach.md b/content/programovani/2-rocnik/06-foreach.md new file mode 100644 index 0000000..1c0a779 --- /dev/null +++ b/content/programovani/2-rocnik/06-foreach.md @@ -0,0 +1,35 @@ +--- +id: cs-2-foreach +nazev: Foreach +popis: Projít každý prvek bez indexu. +--- + +# Výklad + +## Projítí +*foreach* + +- foreach (int z in znamky) vezme prvky od prvního po poslední. +- z je kopie hodnoty. U int se zápisem do z pole nezmění. +- Když index nepotřebujete, foreach je kratší než for. + +> Tip: Součet ve smyčce přičítá aktuální z. + +## Texty +*string[]* + +- Pole může být i ze stringů. +- Stejný foreach funguje pro oba typy, jen se liší typ prvku. +- Pořadí je pořadí v závorce při vytvoření. + +> Tip: Přidejte do pole třetí jméno. + +# Kód + +``` +string[] jmena = { "Anna", "Eva", "Jan" }; +foreach (string jmeno in jmena) +{ + Console.WriteLine(jmeno); +} +``` diff --git a/content/programovani/2-rocnik/07-metody.en.md b/content/programovani/2-rocnik/07-metody.en.md new file mode 100644 index 0000000..3b6fe65 --- /dev/null +++ b/content/programovani/2-rocnik/07-metody.en.md @@ -0,0 +1,4 @@ +--- +nazev: Methods +popis: A named piece of the program you can call. +--- diff --git a/content/programovani/2-rocnik/07-metody.md b/content/programovani/2-rocnik/07-metody.md new file mode 100644 index 0000000..2c0164a --- /dev/null +++ b/content/programovani/2-rocnik/07-metody.md @@ -0,0 +1,37 @@ +--- +id: cs-2-metody +nazev: Metody +popis: Pojmenovaný kus programu, který lze zavolat. +--- + +# Výklad + +## Volání +*jméno()* + +- static void Pozdrav() je metoda bez výsledku. +- Zavolá se Pozdrav(); včetně závorek. +- Bez závorek by to bylo jen jméno, ne volání. + +> Tip: Metodu lze zavolat víckrát a pokaždé udělá totéž. + +## Proč +*jednou* + +- Kód, který se opakuje, patří do metody. +- Jméno metody říká, co dělá: Pozdrav, ne Metoda1. +- void znamená, že nevrací hodnotu. + +> Tip: Přidejte druhé volání Pozdrav(). + +# Kód + +``` +Pozdrav(); +Pozdrav(); + +static void Pozdrav() +{ + Console.WriteLine("Ahoj"); +} +``` diff --git a/content/programovani/2-rocnik/08-navrat.en.md b/content/programovani/2-rocnik/08-navrat.en.md new file mode 100644 index 0000000..8199f32 --- /dev/null +++ b/content/programovani/2-rocnik/08-navrat.en.md @@ -0,0 +1,4 @@ +--- +nazev: Parameters and return +popis: A method receives values and can return one. +--- diff --git a/content/programovani/2-rocnik/08-navrat.md b/content/programovani/2-rocnik/08-navrat.md new file mode 100644 index 0000000..2a9f1cc --- /dev/null +++ b/content/programovani/2-rocnik/08-navrat.md @@ -0,0 +1,38 @@ +--- +id: cs-2-navrat +nazev: Parametry a návrat +popis: Metoda dostane hodnoty a může jednu vrátit. +--- + +# Výklad + +## Parametr +*vstup* + +- int a v závorce je parametr. Volající dodá argument. +- Součet(2, 5) uloží 2 do a a 5 do b. +- Jména parametrů platí jen uvnitř metody. + +> Tip: Změňte argumenty a výsledek se změní, metoda ne. + +## return +*výstup* + +- int před jménem je typ výsledku. +- return hodnotu vrátí a metodu ukončí. +- Volající si výsledek může uložit: int s = Soucet(2, 5); + +> Tip: return 0 omylem na začátku by zbytek metody nespustil. + +# Kód + +``` +int s = Soucet(2, 5); +Console.WriteLine(s); +Console.WriteLine(Soucet(10, 1)); + +static int Soucet(int a, int b) +{ + return a + b; +} +``` diff --git a/content/programovani/3-rocnik/01-trida.en.md b/content/programovani/3-rocnik/01-trida.en.md new file mode 100644 index 0000000..1ac6f34 --- /dev/null +++ b/content/programovani/3-rocnik/01-trida.en.md @@ -0,0 +1,4 @@ +--- +nazev: Class and object +popis: Your own type and one concrete piece of it. +--- diff --git a/content/programovani/3-rocnik/01-trida.md b/content/programovani/3-rocnik/01-trida.md new file mode 100644 index 0000000..54fc345 --- /dev/null +++ b/content/programovani/3-rocnik/01-trida.md @@ -0,0 +1,40 @@ +--- +id: cs-3-trida +nazev: Třída a objekt +popis: Vlastní typ a jeho konkrétní kus. +--- + +# Výklad + +## Třída +*návod* + +- class Zak popisuje, co žák má: jméno a věk. +- new Zak() vytvoří objekt, jeden konkrétní kus. +- Dva new jsou dva objekty. Změna jednoho druhý nemění. + +> Tip: Třída sama o sobě není žák. Je to popis. + +## Pole objektu +*tečka* + +- z.Jmeno se čte a zapisuje přes tečku. +- Na začátek dáme veřejná pole, ať je vidět, komu patří. +- Později je schováme za vlastnost. + +> Tip: Vytvořte druhého žáka a vypište obě jména. + +# Kód + +``` +Zak z = new Zak(); +z.Jmeno = "Anna"; +z.Vek = 17; +Console.WriteLine($"{z.Jmeno}, {z.Vek}"); + +class Zak +{ + public string Jmeno; + public int Vek; +} +``` diff --git a/content/programovani/3-rocnik/02-konstruktor.en.md b/content/programovani/3-rocnik/02-konstruktor.en.md new file mode 100644 index 0000000..22b38d1 --- /dev/null +++ b/content/programovani/3-rocnik/02-konstruktor.en.md @@ -0,0 +1,4 @@ +--- +nazev: Constructor +popis: A method that fills the object immediately. +--- diff --git a/content/programovani/3-rocnik/02-konstruktor.md b/content/programovani/3-rocnik/02-konstruktor.md new file mode 100644 index 0000000..dcd171e --- /dev/null +++ b/content/programovani/3-rocnik/02-konstruktor.md @@ -0,0 +1,44 @@ +--- +id: cs-3-konstruktor +nazev: Konstruktor +popis: Metoda, která objekt hned naplní. +--- + +# Výklad + +## Jméno třídy +*new Zak* + +- Konstruktor se jmenuje jako třída a nemá návratový typ. +- new Zak("Anna", 17) ho zavolá. +- Prázdný new Zak() přestane fungovat, jakmile napíšete vlastní konstruktor. + +> Tip: Hodnoty patří do new, ne až na další řádky. + +## this +*tento objekt* + +- this.Jmeno je pole objektu. Jmeno bez this by v konstruktoru byl parametr. +- Když se jmenují stejně, this je nutný. +- Mimo konstruktor this často není potřeba. + +> Tip: Přidejte do výpisu věk z konstruktoru. + +# Kód + +``` +Zak z = new Zak("Anna", 17); +Console.WriteLine($"{z.Jmeno}, {z.Vek}"); + +class Zak +{ + public string Jmeno; + public int Vek; + + public Zak(string jmeno, int vek) + { + Jmeno = jmeno; + Vek = vek; + } +} +``` diff --git a/content/programovani/3-rocnik/03-vlastnosti.en.md b/content/programovani/3-rocnik/03-vlastnosti.en.md new file mode 100644 index 0000000..d20739a --- /dev/null +++ b/content/programovani/3-rocnik/03-vlastnosti.en.md @@ -0,0 +1,4 @@ +--- +nazev: Properties +popis: get and set instead of a bare field. +--- diff --git a/content/programovani/3-rocnik/03-vlastnosti.md b/content/programovani/3-rocnik/03-vlastnosti.md new file mode 100644 index 0000000..e9cea23 --- /dev/null +++ b/content/programovani/3-rocnik/03-vlastnosti.md @@ -0,0 +1,50 @@ +--- +id: cs-3-vlastnosti +nazev: Vlastnosti +popis: get a set místo holého pole. +--- + +# Výklad + +## get a set +*tečka zůstává* + +- public string Jmeno { get; set; } se zvenku používá jako pole. +- Uvnitř může set odmítnout špatnou hodnotu. +- get hodnotu vrátí, set ji uloží. + +> Tip: Zápis z.Vek = -1 může set zarazit, holé pole ne. + +## Kontrola +*set* + +- V příkladu set u věku záporné číslo neuloží. +- get pořád vrátí poslední platnou hodnotu. +- Tak se objekt nepokazí zvenku. + +> Tip: Zkuste z.Vek = -3 a vypište věk. Zůstane 17. + +# Kód + +``` +Zak z = new Zak("Anna", 17); +z.Vek = -3; +Console.WriteLine(z.Vek); + +class Zak +{ + public string Jmeno { get; set; } + private int vek; + public int Vek + { + get { return vek; } + set { if (value >= 0) vek = value; } + } + + public Zak(string jmeno, int vek) + { + Jmeno = jmeno; + Vek = vek; + } +} +``` diff --git a/content/programovani/3-rocnik/04-zapouzdreni.en.md b/content/programovani/3-rocnik/04-zapouzdreni.en.md new file mode 100644 index 0000000..08bb91c --- /dev/null +++ b/content/programovani/3-rocnik/04-zapouzdreni.en.md @@ -0,0 +1,4 @@ +--- +nazev: Encapsulation +popis: Private data and public methods. +--- diff --git a/content/programovani/3-rocnik/04-zapouzdreni.md b/content/programovani/3-rocnik/04-zapouzdreni.md new file mode 100644 index 0000000..27b5b89 --- /dev/null +++ b/content/programovani/3-rocnik/04-zapouzdreni.md @@ -0,0 +1,49 @@ +--- +id: cs-3-zapouzdreni +nazev: Zapouzdření +popis: Soukromá data a veřejné metody. +--- + +# Výklad + +## private +*schované* + +- private pole vidí jen kód té třídy. +- public metoda je dveře, kterými se s objektem mluví. +- Zvenku z.penize nenapíšete. Zavoláte Pridej. + +> Tip: Když je vše public, kdokoli rozbije pravidla objektu. + +## Metoda +*Pridej* + +- Pridej přičte a vrátí nový stav. +- Záporný vklad lze v metodě odmítnout. +- Výpis stavu jde přes Kolik, ne přes pole. + +> Tip: Zkuste Pridej(-10). Stav se nemá zmenšit. + +# Kód + +``` +Pokladna p = new Pokladna(); +p.Pridej(50); +p.Pridej(-10); +Console.WriteLine(p.Kolik()); + +class Pokladna +{ + private int penize; + + public void Pridej(int castka) + { + if (castka > 0) penize = penize + castka; + } + + public int Kolik() + { + return penize; + } +} +``` diff --git a/content/programovani/3-rocnik/05-dedicnost.en.md b/content/programovani/3-rocnik/05-dedicnost.en.md new file mode 100644 index 0000000..8e3d992 --- /dev/null +++ b/content/programovani/3-rocnik/05-dedicnost.en.md @@ -0,0 +1,4 @@ +--- +nazev: Inheritance +popis: A new class takes over a finished one. +--- diff --git a/content/programovani/3-rocnik/05-dedicnost.md b/content/programovani/3-rocnik/05-dedicnost.md new file mode 100644 index 0000000..6b1029a --- /dev/null +++ b/content/programovani/3-rocnik/05-dedicnost.md @@ -0,0 +1,52 @@ +--- +id: cs-3-dedicnost +nazev: Dědičnost +popis: Nová třída převezme hotovou. +--- + +# Výklad + +## base +*: Zak* + +- class Student : Zak znamená, že student je žák a má něco navíc. +- Dostane Jmeno a Vek, aniž by se psaly znovu. +- base(...) zavolá konstruktor předka. + +> Tip: Student umí to, co Zak, a ještě obor. + +## Je +*typ* + +- Student lze uložit do proměnné Zak. Pořád je to student. +- Opačně žák bez oboru studentem není. +- Na specialitu sáhnete, až když máte původní typ. + +> Tip: Vypište i obor. Patří studentovi, ne obecnému žákovi. + +# Kód + +``` +Student s = new Student("Eva", 18, "informatika"); +Console.WriteLine($"{s.Jmeno}, {s.Obor}"); + +class Zak +{ + public string Jmeno { get; set; } + public int Vek { get; set; } + public Zak(string jmeno, int vek) + { + Jmeno = jmeno; + Vek = vek; + } +} + +class Student : Zak +{ + public string Obor { get; set; } + public Student(string jmeno, int vek, string obor) : base(jmeno, vek) + { + Obor = obor; + } +} +``` diff --git a/content/programovani/3-rocnik/06-polymorfismus.en.md b/content/programovani/3-rocnik/06-polymorfismus.en.md new file mode 100644 index 0000000..61e4f62 --- /dev/null +++ b/content/programovani/3-rocnik/06-polymorfismus.en.md @@ -0,0 +1,4 @@ +--- +nazev: Polymorphism +popis: The same call, a different answer from the child. +--- diff --git a/content/programovani/3-rocnik/06-polymorfismus.md b/content/programovani/3-rocnik/06-polymorfismus.md new file mode 100644 index 0000000..f2b4bb7 --- /dev/null +++ b/content/programovani/3-rocnik/06-polymorfismus.md @@ -0,0 +1,50 @@ +--- +id: cs-3-polymorfismus +nazev: Polymorfismus +popis: Stejné volání, jiná odpověď potomka. +--- + +# Výklad + +## virtual a override +*přepsání* + +- virtual u předka dovolí potomkovi metodu vyměnit. +- override u potomka řekne, že jde o tu samou metodu. +- Bez virtual by potomek jen schoval jméno a přes typ předka by se volala stará verze. + +> Tip: Proměnná je Zvire, objekt je Pes. Zavolá se štěk, ne obecné zvíře. + +## Seznam +*víc tvarů* + +- Do pole Zvire lze dát psa i kočku. +- foreach volá Zvuk a každý objekt odpoví po svém. +- Nové zvíře přidáte třídou, ne úpravou smyčky. + +> Tip: Přidejte do pole kočku a třetí řádek výstupu bude mňau. + +# Kód + +``` +Zvire[] zvirata = { new Pes(), new Kocka() }; +foreach (Zvire z in zvirata) +{ + Console.WriteLine(z.Zvuk()); +} + +class Zvire +{ + public virtual string Zvuk() { return "..."; } +} + +class Pes : Zvire +{ + public override string Zvuk() { return "haf"; } +} + +class Kocka : Zvire +{ + public override string Zvuk() { return "mňau"; } +} +``` diff --git a/content/programovani/3-rocnik/07-rozhrani.en.md b/content/programovani/3-rocnik/07-rozhrani.en.md new file mode 100644 index 0000000..1f39bf6 --- /dev/null +++ b/content/programovani/3-rocnik/07-rozhrani.en.md @@ -0,0 +1,4 @@ +--- +nazev: Interface +popis: A contract: which methods a type must have. +--- diff --git a/content/programovani/3-rocnik/07-rozhrani.md b/content/programovani/3-rocnik/07-rozhrani.md new file mode 100644 index 0000000..0caebee --- /dev/null +++ b/content/programovani/3-rocnik/07-rozhrani.md @@ -0,0 +1,50 @@ +--- +id: cs-3-rozhrani +nazev: Rozhraní +popis: Smlouva: které metody typ musí mít. +--- + +# Výklad + +## interface +*I* + +- interface nemá hotový kód, jen hlavičky metod. +- class Pes : IMluvici slíbí, že Mluv napíše. +- Jedna třída může splnit víc rozhraní. Dědit může jen jednu třídu. + +> Tip: Jméno rozhraní se často píše s I na začátku: IMluvici. + +## Společný typ +*seznam* + +- Proměnná typu rozhraní udrží kohokoli, kdo smlouvu splnil. +- Pes a Radio nemají společného předka, ale oba umí Mluv. +- Smyčka jim rozumí přes smlouvu, ne přes původ. + +> Tip: Rozhraní neříká, jak metoda funguje. To napíše až třída. + +# Kód + +``` +IMluvici[] veci = { new Pes(), new Radio() }; +foreach (IMluvici v in veci) +{ + Console.WriteLine(v.Mluv()); +} + +interface IMluvici +{ + string Mluv(); +} + +class Pes : IMluvici +{ + public string Mluv() { return "haf"; } +} + +class Radio : IMluvici +{ + public string Mluv() { return "hudba"; } +} +``` diff --git a/content/programovani/3-rocnik/08-enum.en.md b/content/programovani/3-rocnik/08-enum.en.md new file mode 100644 index 0000000..4fb204b --- /dev/null +++ b/content/programovani/3-rocnik/08-enum.en.md @@ -0,0 +1,4 @@ +--- +nazev: Enum +popis: A fixed list of named values. +--- diff --git a/content/programovani/3-rocnik/08-enum.md b/content/programovani/3-rocnik/08-enum.md new file mode 100644 index 0000000..dec3916 --- /dev/null +++ b/content/programovani/3-rocnik/08-enum.md @@ -0,0 +1,50 @@ +--- +id: cs-3-enum +nazev: Výčet +popis: Pevný seznam pojmenovaných hodnot. +--- + +# Výklad + +## enum +*Den* + +- enum Den { Po, Ut, St } jsou tři pojmenované hodnoty. +- Místo magického čísla 1 píšete Den.Po. +- Překladač nepustí hodnotu, která ve výčtu není. + +> Tip: Čísla 0, 1, 2 by za měsíc nikdo nepřečetl. Jména ano. + +## switch +*přes výčet* + +- switch na enum má case pro každou hodnotu. +- Když přidáte čtvrtek a zapomenete case, překlad na to v novém zápisu upozorní. +- Výčet je typ. Není to string. + +> Tip: Změňte dnes na Den.St. + +# Kód + +``` +Den dnes = Den.Po; +switch (dnes) +{ + case Den.Po: + Console.WriteLine("pondělí"); + break; + case Den.Ut: + Console.WriteLine("úterý"); + break; + default: + Console.WriteLine("jiný den"); + break; +} + +enum Den +{ + Po, + Ut, + St +} +``` diff --git a/content/programovani/4-rocnik/01-vyjimky.en.md b/content/programovani/4-rocnik/01-vyjimky.en.md new file mode 100644 index 0000000..c4750a7 --- /dev/null +++ b/content/programovani/4-rocnik/01-vyjimky.en.md @@ -0,0 +1,4 @@ +--- +nazev: Exceptions +popis: An error you can catch and explain. +--- diff --git a/content/programovani/4-rocnik/01-vyjimky.md b/content/programovani/4-rocnik/01-vyjimky.md new file mode 100644 index 0000000..117182a --- /dev/null +++ b/content/programovani/4-rocnik/01-vyjimky.md @@ -0,0 +1,43 @@ +--- +id: cs-4-vyjimky +nazev: Výjimky +popis: Chyba, kterou lze chytit a vysvětlit. +--- + +# Výklad + +## try +*pokus* + +- try je kus, který může spadnout. +- catch (Exception e) ho chytí, místo aby spadl celý program. +- e.Message je text chyby. + +> Tip: Bez try by Parse na textu abc program ukončil. + +## finally +*vždy* + +- finally se spustí, ať try prošel, nebo ne. +- Hodí se na úklid. V příkladu jen vypíše, že končíme. +- catch může být víc, pro různé typy chyb. + +> Tip: Změňte abc na 5. catch se přeskočí, finally ne. + +# Kód + +``` +try +{ + int n = int.Parse("abc"); + Console.WriteLine(n); +} +catch (Exception e) +{ + Console.WriteLine("Chyba: " + e.Message); +} +finally +{ + Console.WriteLine("konec"); +} +``` diff --git a/content/programovani/4-rocnik/02-list.en.md b/content/programovani/4-rocnik/02-list.en.md new file mode 100644 index 0000000..0c8016d --- /dev/null +++ b/content/programovani/4-rocnik/02-list.en.md @@ -0,0 +1,4 @@ +--- +nazev: List +popis: A list that can grow. +--- diff --git a/content/programovani/4-rocnik/02-list.md b/content/programovani/4-rocnik/02-list.md new file mode 100644 index 0000000..16cbf92 --- /dev/null +++ b/content/programovani/4-rocnik/02-list.md @@ -0,0 +1,39 @@ +--- +id: cs-4-list +nazev: List +popis: Seznam, který umí růst. +--- + +# Výklad + +## Přidávání +*Add* + +- List začne prázdný. Add přidá na konec. +- Count je počet. Indexy jsou zase od nuly. +- Na rozdíl od pole délku dopředu neříkáte. + +> Tip: List potřebuje špičaté závorky s typem prvku. + +## Odebrání +*Remove* + +- Remove("Eva") odebere první Evu. +- RemoveAt(0) odebere podle indexu. +- foreach pak projde, co zůstalo. + +> Tip: Přidejte třetí jméno před odebráním. + +# Kód + +``` +List jmena = new List(); +jmena.Add("Anna"); +jmena.Add("Eva"); +jmena.Remove("Eva"); +foreach (string j in jmena) +{ + Console.WriteLine(j); +} +Console.WriteLine(jmena.Count); +``` diff --git a/content/programovani/4-rocnik/03-slovnik.en.md b/content/programovani/4-rocnik/03-slovnik.en.md new file mode 100644 index 0000000..40451f5 --- /dev/null +++ b/content/programovani/4-rocnik/03-slovnik.en.md @@ -0,0 +1,4 @@ +--- +nazev: Dictionary +popis: A value under a key. +--- diff --git a/content/programovani/4-rocnik/03-slovnik.md b/content/programovani/4-rocnik/03-slovnik.md new file mode 100644 index 0000000..4ff2289 --- /dev/null +++ b/content/programovani/4-rocnik/03-slovnik.md @@ -0,0 +1,39 @@ +--- +id: cs-4-slovnik +nazev: Slovník +popis: Hodnota pod klíčem. +--- + +# Výklad + +## Klíč +*jednou* + +- Dictionary má klíč text a hodnotu číslo. +- znamky["Anna"] = 1 uloží nebo přepíše. +- Stejný klíč je v slovníku jen jednou. + +> Tip: Pole je podle čísla. Slovník je podle jména, které si zvolíte. + +## Čtení +*ContainsKey* + +- Čtení chybějícího klíče spadne. +- ContainsKey napřed zjistí, jestli klíč je. +- foreach projde dvojice Key a Value. + +> Tip: Zeptejte se na jméno, které v slovníku není, až po ContainsKey. + +# Kód + +``` +Dictionary znamky = new Dictionary(); +znamky["Anna"] = 1; +znamky["Eva"] = 2; +znamky["Anna"] = 3; +if (znamky.ContainsKey("Eva")) +{ + Console.WriteLine(znamky["Eva"]); +} +Console.WriteLine(znamky["Anna"]); +``` diff --git a/content/programovani/4-rocnik/04-linq.en.md b/content/programovani/4-rocnik/04-linq.en.md new file mode 100644 index 0000000..0b2ec5a --- /dev/null +++ b/content/programovani/4-rocnik/04-linq.en.md @@ -0,0 +1,4 @@ +--- +nazev: LINQ +popis: Queries over a list. +--- diff --git a/content/programovani/4-rocnik/04-linq.md b/content/programovani/4-rocnik/04-linq.md new file mode 100644 index 0000000..de4a297 --- /dev/null +++ b/content/programovani/4-rocnik/04-linq.md @@ -0,0 +1,36 @@ +--- +id: cs-4-linq +nazev: LINQ +popis: Dotazy nad seznamem. +--- + +# Výklad + +## Where +*filtr* + +- Where nechá jen prvky, které splní podmínku. +- n => n >= 10 je krátká funkce: vezmi n a vrať, jestli je aspoň 10. +- Samotné Where ještě nic neprojde. ToList nebo foreach výsledek vezme. + +> Tip: Šipka => není porovnání. Je to zápis malé funkce. + +## Select +*převod* + +- Select z každého prvku udělá jiný. +- OrderBy seřadí. +- Dotaz lze řetězit: Where a pak Select. + +> Tip: Změňte 10 na 12 a dvojka z výpisu zmizí. + +# Kód + +``` +int[] cisla = { 4, 11, 7, 20 }; +var velka = cisla.Where(n => n >= 10).OrderBy(n => n); +foreach (int n in velka) +{ + Console.WriteLine(n); +} +``` diff --git a/content/programovani/4-rocnik/05-soubory.en.md b/content/programovani/4-rocnik/05-soubory.en.md new file mode 100644 index 0000000..f0b0642 --- /dev/null +++ b/content/programovani/4-rocnik/05-soubory.en.md @@ -0,0 +1,4 @@ +--- +nazev: Files +popis: Writing and reading text. +--- diff --git a/content/programovani/4-rocnik/05-soubory.md b/content/programovani/4-rocnik/05-soubory.md new file mode 100644 index 0000000..bb0133a --- /dev/null +++ b/content/programovani/4-rocnik/05-soubory.md @@ -0,0 +1,33 @@ +--- +id: cs-4-soubory +nazev: Soubory +popis: Zápis a čtení textu. +--- + +# Výklad + +## WriteAllText +*jednou* + +- File.WriteAllText zapíše celý soubor. Když existuje, přepíše ho. +- Cesta pozdrav.txt je vedle programu, v jeho složce. +- ReadAllText celý text vrátí jako string. + +> Tip: Na telefonu se soubor nespustí. Na počítači s .NET ano. + +## Řádky +*Append* + +- AppendAllText přidá na konec a soubor nesmaže. +- ReadAllLines vrátí pole řádků. +- Po skončení programu soubor ve složce laboratoře zůstane. + +> Tip: Spusťte dvakrát. Druhý zápis WriteAllText starý text nahradí. + +# Kód + +``` +File.WriteAllText("pozdrav.txt", "Ahoj ze souboru"); +string zpet = File.ReadAllText("pozdrav.txt"); +Console.WriteLine(zpet); +``` diff --git a/content/programovani/4-rocnik/06-generika.en.md b/content/programovani/4-rocnik/06-generika.en.md new file mode 100644 index 0000000..422ce16 --- /dev/null +++ b/content/programovani/4-rocnik/06-generika.en.md @@ -0,0 +1,4 @@ +--- +nazev: Generics +popis: One method for several types. +--- diff --git a/content/programovani/4-rocnik/06-generika.md b/content/programovani/4-rocnik/06-generika.md new file mode 100644 index 0000000..dfe9d7d --- /dev/null +++ b/content/programovani/4-rocnik/06-generika.md @@ -0,0 +1,37 @@ +--- +id: cs-4-generika +nazev: Generika +popis: Jedna metoda pro víc typů. +--- + +# Výklad + +## T +*typový parametr* + +- static void Vypis(T hodnota) přijme int i string. +- T se doplní při volání podle argumentu. +- Jedna metoda tak nenutí kopírovat kód pro každý typ. + +> Tip: Když metoda s T má umět jen porovnávat, T se omezí. Tady stačí výpis. + +## List +*už ho znáte* + +- List je generická třída. string je dosazený T. +- Stejný List drží čísla a metody jsou stejné. +- Generika hlídá typ. Do List string nedáte. + +> Tip: Zavolejte Vypis s číslem i s textem. + +# Kód + +``` +Vypis(7); +Vypis("ahoj"); + +static void Vypis(T hodnota) +{ + Console.WriteLine(hodnota); +} +``` diff --git a/content/programovani/4-rocnik/07-delegaty.en.md b/content/programovani/4-rocnik/07-delegaty.en.md new file mode 100644 index 0000000..d39c31f --- /dev/null +++ b/content/programovani/4-rocnik/07-delegaty.en.md @@ -0,0 +1,4 @@ +--- +nazev: Delegates +popis: A variable that holds a method. +--- diff --git a/content/programovani/4-rocnik/07-delegaty.md b/content/programovani/4-rocnik/07-delegaty.md new file mode 100644 index 0000000..b5b9652 --- /dev/null +++ b/content/programovani/4-rocnik/07-delegaty.md @@ -0,0 +1,34 @@ +--- +id: cs-4-delegaty +nazev: Delegáty +popis: Proměnná, do které uložíte metodu. +--- + +# Výklad + +## Func +*funkce* + +- Func je metoda, která dostane int a vrátí int. +- x => x * x je taková metoda bez jména. +- Do proměnné ji uložíte a zavoláte později. + +> Tip: Závorky pocet(4) metodu spustí. Bez nich byste ji jen předali dál. + +## Action +*bez výsledku* + +- Action nic nevrací. Hodí se na kroky. +- Metodu můžete předat jiné metodě jako argument. +- Tak se píše i Where z minulé lekce. + +> Tip: Vyměňte x * x za x + 1. + +# Kód + +``` +Func pocet = x => x * x; +Console.WriteLine(pocet(4)); +Action pozdrav = () => Console.WriteLine("ahoj"); +pozdrav(); +``` diff --git a/content/programovani/4-rocnik/08-async.en.md b/content/programovani/4-rocnik/08-async.en.md new file mode 100644 index 0000000..98da3e9 --- /dev/null +++ b/content/programovani/4-rocnik/08-async.en.md @@ -0,0 +1,4 @@ +--- +nazev: Async and await +popis: Work that waits without freezing the way you write it. +--- diff --git a/content/programovani/4-rocnik/08-async.md b/content/programovani/4-rocnik/08-async.md new file mode 100644 index 0000000..9baebaf --- /dev/null +++ b/content/programovani/4-rocnik/08-async.md @@ -0,0 +1,33 @@ +--- +id: cs-4-async +nazev: Async a await +popis: Práce, která počká, aniž by zamkla zbytek zápisu. +--- + +# Výklad + +## await +*počkej* + +- await Task.Delay čeká, ale zápis zůstane řádek za řádkem. +- Metoda s await má v hlavičce async a vrací Task. +- V krátkém programu smí await stát i na nejvyšší úrovni. + +> Tip: Delay(300) čeká 300 milisekund. Výpis hotovo přijde až po něm. + +## Task +*slib* + +- Task je slib, že práce doběhne. +- await slib rozbalí. Bez await byste měli slib, ne výsledek. +- Na síť a soubory se await hodí. Na 2 + 2 ne. + +> Tip: Dejte za první výpis ještě jeden await a druhý řádek. + +# Kód + +``` +Console.WriteLine("začátek"); +await Task.Delay(300); +Console.WriteLine("hotovo"); +``` diff --git a/ios/Graduately/AppModel.swift b/ios/Graduately/AppModel.swift index bfe0b40..774dddd 100644 --- a/ios/Graduately/AppModel.swift +++ b/ios/Graduately/AppModel.swift @@ -254,6 +254,26 @@ final class AppModel: ObservableObject { refresh() } + func markCs(_ id: Int) { + progress.markCs(id) + refresh() + } + + func markCs2(_ id: Int) { + progress.markCs2(id) + refresh() + } + + func markCs3(_ id: Int) { + progress.markCs3(id) + refresh() + } + + func markCs4(_ id: Int) { + progress.markCs4(id) + refresh() + } + func markMat0(_ id: Int) { progress.markMat0(id) refresh() diff --git a/ios/Graduately/Data/Content.swift b/ios/Graduately/Data/Content.swift index 4b1aa1e..4cb54aa 100644 --- a/ios/Graduately/Data/Content.swift +++ b/ios/Graduately/Data/Content.swift @@ -63,6 +63,10 @@ final class Content { let tg2: [J] let tg3: [J] let tg4: [J] + let cs: [J] + let cs2: [J] + let cs3: [J] + let cs4: [J] let mat0: [J] let mat: [J] let mat2: [J] @@ -133,6 +137,10 @@ final class Content { tg2 = raw.arr("tg2") tg3 = raw.arr("tg3") tg4 = raw.arr("tg4") + cs = raw.arr("cs") + cs2 = raw.arr("cs2") + cs3 = raw.arr("cs3") + cs4 = raw.arr("cs4") mat0 = raw.arr("mat0") mat = raw.arr("mat") mat2 = raw.arr("mat2") @@ -199,6 +207,10 @@ final class Content { func tg2Lesson(_ id: Int) -> J? { tg2.first { $0.int("id") == id } } func tg3Lesson(_ id: Int) -> J? { tg3.first { $0.int("id") == id } } func tg4Lesson(_ id: Int) -> J? { tg4.first { $0.int("id") == id } } + func csLesson(_ id: Int) -> J? { cs.first { $0.int("id") == id } } + func cs2Lesson(_ id: Int) -> J? { cs2.first { $0.int("id") == id } } + func cs3Lesson(_ id: Int) -> J? { cs3.first { $0.int("id") == id } } + func cs4Lesson(_ id: Int) -> J? { cs4.first { $0.int("id") == id } } func mat0Lesson(_ id: Int) -> J? { mat0.first { $0.int("id") == id } } func matLesson(_ id: Int) -> J? { mat.first { $0.int("id") == id } } func mat2Lesson(_ id: Int) -> J? { mat2.first { $0.int("id") == id } } diff --git a/ios/Graduately/Data/ProgressStore.swift b/ios/Graduately/Data/ProgressStore.swift index c7ea326..50d8033 100644 --- a/ios/Graduately/Data/ProgressStore.swift +++ b/ios/Graduately/Data/ProgressStore.swift @@ -102,6 +102,14 @@ final class ProgressStore { func markTg3(_ id: Int) { mark("tg3.\(id)") } func tg4Done(_ id: Int) -> Bool { done("tg4.\(id)") } func markTg4(_ id: Int) { mark("tg4.\(id)") } + func csDone(_ id: Int) -> Bool { done("cs.\(id)") } + func markCs(_ id: Int) { mark("cs.\(id)") } + func cs2Done(_ id: Int) -> Bool { done("cs2.\(id)") } + func markCs2(_ id: Int) { mark("cs2.\(id)") } + func cs3Done(_ id: Int) -> Bool { done("cs3.\(id)") } + func markCs3(_ id: Int) { mark("cs3.\(id)") } + func cs4Done(_ id: Int) -> Bool { done("cs4.\(id)") } + func markCs4(_ id: Int) { mark("cs4.\(id)") } func mat0Done(_ id: Int) -> Bool { done("mat0.\(id)") } func markMat0(_ id: Int) { mark("mat0.\(id)") } func matDone(_ id: Int) -> Bool { done("mat.\(id)") } @@ -328,6 +336,38 @@ func summarize(_ content: Content, _ p: ProgressStore) -> ProgressSum { sum.doneUnits += 1 } } + for l in content.cs { + sum.totalEx += 1 + sum.openUnits += 1 + if p.csDone(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.cs2 { + sum.totalEx += 1 + sum.openUnits += 1 + if p.cs2Done(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.cs3 { + sum.totalEx += 1 + sum.openUnits += 1 + if p.cs3Done(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.cs4 { + sum.totalEx += 1 + sum.openUnits += 1 + if p.cs4Done(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } for l in content.mat0 { sum.totalEx += 1 sum.openUnits += 1 diff --git a/ios/Graduately/Route.swift b/ios/Graduately/Route.swift index 61a8c0b..3e3b2bc 100644 --- a/ios/Graduately/Route.swift +++ b/ios/Graduately/Route.swift @@ -124,6 +124,15 @@ enum Route: Hashable { case tg4Map case tg4Lesson(Int) case tg4Ex(Int) + case csYears + case csMap + case csLesson(Int) + case cs2Map + case cs2Lesson(Int) + case cs3Map + case cs3Lesson(Int) + case cs4Map + case cs4Lesson(Int) case czechMap case mluvnice case mluvEx(Int) @@ -250,6 +259,15 @@ enum Route: Hashable { case .tg4Map: return "tg4map" case .tg4Lesson(let id): return "tg4unit\(id)" case .tg4Ex(let id): return "tg4ex\(id)" + case .csYears: return "csyears" + case .csMap: return "csmap" + case .csLesson(let id): return "csunit\(id)" + case .cs2Map: return "cs2map" + case .cs2Lesson(let id): return "cs2unit\(id)" + case .cs3Map: return "cs3map" + case .cs3Lesson(let id): return "cs3unit\(id)" + case .cs4Map: return "cs4map" + case .cs4Lesson(let id): return "cs4unit\(id)" case .czechMap: return "czechmap" case .mluvnice: return "mluvnice" case .mluvEx(let n): return "mluve\(n)" @@ -500,6 +518,24 @@ enum Route: Hashable { return [.tgYears, .tg4Map, .tg4Lesson(id)] case .tg4Ex(let id): return [.tgYears, .tg4Map, .tg4Lesson(id), .tg4Ex(id)] + case .csYears: + return [.csYears] + case .csMap: + return [.csYears, .csMap] + case .csLesson(let id): + return [.csYears, .csMap, .csLesson(id)] + case .cs2Map: + return [.csYears, .cs2Map] + case .cs2Lesson(let id): + return [.csYears, .cs2Map, .cs2Lesson(id)] + case .cs3Map: + return [.csYears, .cs3Map] + case .cs3Lesson(let id): + return [.csYears, .cs3Map, .cs3Lesson(id)] + case .cs4Map: + return [.csYears, .cs4Map] + case .cs4Lesson(let id): + return [.csYears, .cs4Map, .cs4Lesson(id)] case .czechMap: return [.czechMap] case .mluvnice: @@ -565,6 +601,11 @@ func routeFromPage(_ page: String) -> Route? { case "tg2map": return .tg2Map case "tg3map": return .tg3Map case "tg4map": return .tg4Map + case "csyears": return .csYears + case "csmap": return .csMap + case "cs2map": return .cs2Map + case "cs3map": return .cs3Map + case "cs4map": return .cs4Map case "czechmap": return .czechMap case "mluvnice": return .mluvnice case "readinglist": return .readingList @@ -698,6 +739,18 @@ func routeFromPage(_ page: String) -> Route? { if let m = page.wholeMatch(of: /tgex(\d+)/), let n = Int(m.1) { return .tgEx(n) } + if let m = page.wholeMatch(of: /cs4unit(\d+)/), let n = Int(m.1) { + return .cs4Lesson(n) + } + if let m = page.wholeMatch(of: /cs3unit(\d+)/), let n = Int(m.1) { + return .cs3Lesson(n) + } + if let m = page.wholeMatch(of: /cs2unit(\d+)/), let n = Int(m.1) { + return .cs2Lesson(n) + } + if let m = page.wholeMatch(of: /csunit(\d+)/), let n = Int(m.1) { + return .csLesson(n) + } if let m = page.wholeMatch(of: /lit4unit(\d+)/), let n = Int(m.1) { return .lit4Lesson(n) } diff --git a/ios/Graduately/UI/Components.swift b/ios/Graduately/UI/Components.swift index 2765396..a4bd6fe 100644 --- a/ios/Graduately/UI/Components.swift +++ b/ios/Graduately/UI/Components.swift @@ -680,6 +680,7 @@ func subjectSymbol(_ icon: String) -> String { case "bolt": return "bolt.fill" case "function": return "function" case "ruler": return "ruler.fill" + case "code": return "chevron.left.forwardslash.chevron.right" default: return "lock.fill" } } diff --git a/ios/Graduately/UI/Screens.swift b/ios/Graduately/UI/Screens.swift index 044065b..e2f1ebb 100644 --- a/ios/Graduately/UI/Screens.swift +++ b/ios/Graduately/UI/Screens.swift @@ -713,6 +713,12 @@ private func subjectSum(_ vm: AppModel, _ s: J) -> ProgressSum { + vm.content.mat2.filter { vm.progress.mat2Done($0.int("id")) }.count + vm.content.mat3.filter { vm.progress.mat3Done($0.int("id")) }.count + vm.content.mat4.filter { vm.progress.mat4Done($0.int("id")) }.count + case "csyears", "csmap", "cs2map", "cs3map", "cs4map": + sum.totalEx = vm.content.cs.count + vm.content.cs2.count + vm.content.cs3.count + vm.content.cs4.count + sum.doneEx = vm.content.cs.filter { vm.progress.csDone($0.int("id")) }.count + + vm.content.cs2.filter { vm.progress.cs2Done($0.int("id")) }.count + + vm.content.cs3.filter { vm.progress.cs3Done($0.int("id")) }.count + + vm.content.cs4.filter { vm.progress.cs4Done($0.int("id")) }.count case "tgyears", "tgmap", "tg2map", "tg3map", "tg4map": sum.totalEx = vm.content.tg.count + vm.content.tg2.count + vm.content.tg3.count + vm.content.tg4.count sum.doneEx = vm.content.tg.filter { vm.progress.tgDone($0.int("id")) }.count @@ -1042,6 +1048,23 @@ private func buildSearch(_ vm: AppModel) -> [Hit] { add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "tg4unit\(l.int("id"))", extra: "autocad") add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tg4ex\(l.int("id"))") } + add(vm.tr("Programování"), vm.tr("search_lesson"), "csyears", extra: "csharp c#") + add(vm.tr("cs_year1"), vm.tr("search_lesson"), "csmap", extra: "rocnik") + add(vm.tr("cs_year2"), vm.tr("search_lesson"), "cs2map", extra: "rocnik") + add(vm.tr("cs_year3"), vm.tr("search_lesson"), "cs3map", extra: "rocnik") + add(vm.tr("cs_year4"), vm.tr("search_lesson"), "cs4map", extra: "rocnik") + for l in vm.content.cs { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "csunit\(l.int("id"))", extra: "csharp") + } + for l in vm.content.cs2 { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "cs2unit\(l.int("id"))", extra: "csharp") + } + for l in vm.content.cs3 { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "cs3unit\(l.int("id"))", extra: "csharp") + } + for l in vm.content.cs4 { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "cs4unit\(l.int("id"))", extra: "csharp") + } add(vm.tr("Chemie"), vm.tr("search_lesson"), "chemmap", extra: "chemie") add(vm.tr("Biologie"), vm.tr("search_lesson"), "biomap", extra: "biologie") for l in vm.content.chem { @@ -1776,6 +1799,47 @@ struct MatYearsScreen: View { } } +struct CsYearsScreen: View { + @ObservedObject var vm: AppModel + + var body: some View { + #if os(macOS) + let years: [(String, String, Route)] = [ + ("cs_year1", "cs_sub", .csMap), + ("cs_year2", "cs2_sub", .cs2Map), + ("cs_year3", "cs3_sub", .cs3Map), + ("cs_year4", "cs4_sub", .cs4Map), + ] + MacFillGrid(count: years.count, minWidth: 260, minHeight: 180) { i in + NavigationLink(value: years[i].2) { + MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).") + } + .buttonStyle(PressScaleStyle()) + } + .navigationTitle(vm.tr("Programování")) + .navigationSubtitle(vm.tr("cs_years_sub")) + #else + List { + NavigationLink(value: Route.csMap) { + ListRowLabel(title: vm.tr("cs_year1"), subtitle: vm.tr("cs_sub")) + } + NavigationLink(value: Route.cs2Map) { + ListRowLabel(title: vm.tr("cs_year2"), subtitle: vm.tr("cs2_sub")) + } + NavigationLink(value: Route.cs3Map) { + ListRowLabel(title: vm.tr("cs_year3"), subtitle: vm.tr("cs3_sub")) + } + NavigationLink(value: Route.cs4Map) { + ListRowLabel(title: vm.tr("cs_year4"), subtitle: vm.tr("cs4_sub")) + } + } + .navigationTitle(vm.tr("Programování")) + .navigationSubtitle(vm.tr("cs_years_sub")) + .appListStyle() + #endif + } +} + struct TgYearsScreen: View { @ObservedObject var vm: AppModel @@ -2876,6 +2940,64 @@ struct RouteDestination: View { } case .fyz4Ex(let id): FyzQuizScreen(vm: vm, year: 4, id: id) + case .csYears: + CsYearsScreen(vm: vm) + case .csMap: + LessonListScreen( + vm: vm, + title: vm.tr("cs_year1"), + subtitle: vm.tr("cs_sub"), + rows: vm.content.cs.map { l in + let id = l.int("id") + return LessonRow(id: "c\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.csDone(id), destination: .csLesson(id)) + } + ) + case .csLesson(let id): + if let l = vm.content.csLesson(id) { + CodeLessonScreen(vm: vm, lesson: l, onSuccess: { vm.markCs(id) }) + } + case .cs2Map: + LessonListScreen( + vm: vm, + title: vm.tr("cs_year2"), + subtitle: vm.tr("cs2_sub"), + rows: vm.content.cs2.map { l in + let id = l.int("id") + return LessonRow(id: "c2\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.cs2Done(id), destination: .cs2Lesson(id)) + } + ) + case .cs2Lesson(let id): + if let l = vm.content.cs2Lesson(id) { + CodeLessonScreen(vm: vm, lesson: l, onSuccess: { vm.markCs2(id) }) + } + case .cs3Map: + LessonListScreen( + vm: vm, + title: vm.tr("cs_year3"), + subtitle: vm.tr("cs3_sub"), + rows: vm.content.cs3.map { l in + let id = l.int("id") + return LessonRow(id: "c3\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.cs3Done(id), destination: .cs3Lesson(id)) + } + ) + case .cs3Lesson(let id): + if let l = vm.content.cs3Lesson(id) { + CodeLessonScreen(vm: vm, lesson: l, onSuccess: { vm.markCs3(id) }) + } + case .cs4Map: + LessonListScreen( + vm: vm, + title: vm.tr("cs_year4"), + subtitle: vm.tr("cs4_sub"), + rows: vm.content.cs4.map { l in + let id = l.int("id") + return LessonRow(id: "c4\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.cs4Done(id), destination: .cs4Lesson(id)) + } + ) + case .cs4Lesson(let id): + if let l = vm.content.cs4Lesson(id) { + CodeLessonScreen(vm: vm, lesson: l, onSuccess: { vm.markCs4(id) }) + } case .tgYears: TgYearsScreen(vm: vm) case .tgMap: @@ -2970,3 +3092,251 @@ struct RouteDestination: View { } } } + +struct CodeLessonScreen: View { + @ObservedObject var vm: AppModel + let lesson: J + let onSuccess: () -> Void + @State private var code = "" + @State private var stdin = "" + @State private var output = "" + @State private var running = false + + var body: some View { + GeometryReader { geo in + if geo.size.width >= 840 { + HStack(alignment: .top, spacing: 16) { + explanation.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + editor.frame(maxWidth: .infinity, maxHeight: .infinity) + outputPane.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + } + } else { + ScrollView { + VStack(alignment: .leading, spacing: 16) { + explanation + editor.frame(minHeight: 240) + outputPane.frame(minHeight: 160) + } + } + } + } + .padding(16) + .detailScreen(vm.tr(lesson.str("titleKey")), subtitle: vm.tr(lesson.str("subKey"))) + .onAppear { + if code.isEmpty { code = lesson.str("code") } + } + } + + private var explanation: some View { + ScrollView { + VStack(alignment: .leading, spacing: 16) { + ForEach(Array(lesson.arr("slides").enumerated()), id: \.offset) { _, slide in + VStack(alignment: .leading, spacing: 8) { + Text(slide.str("kicker")) + .font(.subheadline.weight(.semibold)) + .foregroundStyle(Color.accentColor) + Text(slide.str("title")) + .font(.title3.bold()) + ForEach(slide.strs("lines"), id: \.self) { line in + Text("• \(line)") + .frame(maxWidth: .infinity, alignment: .leading) + } + if let tip = slide.strOrNull("tip") { + Text(tip) + .foregroundStyle(.secondary) + } + } + } + } + } + } + + private var editor: some View { + VStack(alignment: .leading, spacing: 8) { + Text(vm.tr("cs_code")).font(.headline) + TextEditor(text: $code) + .font(.system(.body, design: .monospaced)) + .scrollContentBackground(.hidden) + .padding(8) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(codeFill, in: RoundedRectangle(cornerRadius: 12)) + Text(vm.tr("cs_stdin")) + .font(.caption) + .foregroundStyle(.secondary) + TextField("", text: $stdin, axis: .vertical) + .font(.system(.body, design: .monospaced)) + .lineLimit(2...4) + .textFieldStyle(.roundedBorder) + } + } + + private var outputPane: some View { + VStack(alignment: .leading, spacing: 8) { + HStack { + Button(vm.tr("cs_run")) { run() } + .buttonStyle(.borderedProminent) + .disabled(running) + Button(vm.tr("cs_reset")) { code = lesson.str("code") } + .disabled(running) + } + Text(vm.tr("cs_output")).font(.headline) + ScrollView { + Text(output.isEmpty ? vm.tr("cs_empty") : output) + .font(.system(.body, design: .monospaced)) + .frame(maxWidth: .infinity, alignment: .topLeading) + .textSelection(.enabled) + } + .padding(12) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + .background(codeFill, in: RoundedRectangle(cornerRadius: 12)) + } + } + + private var codeFill: Color { + #if os(macOS) + Color(.textBackgroundColor).opacity(0.55) + #else + Color(.secondarySystemBackground) + #endif + } + + private func run() { + running = true + output = vm.tr("cs_running") + CSharpRunner.run(code, stdin: stdin) { status, text in + running = false + switch status { + case "unavailable": + output = vm.tr("cs_unavailable") + case "timeout": + output = (text + "\n\n" + vm.tr("cs_timeout")).trimmingCharacters(in: .whitespacesAndNewlines) + default: + output = text.isEmpty ? vm.tr("cs_empty") : text + } + if status == "ok" { onSuccess() } + } + } +} + +enum CSharpRunner { + static func run(_ source: String, stdin: String, done: @escaping (String, String) -> Void) { + #if os(macOS) + DispatchQueue.global(qos: .userInitiated).async { + let result = runMac(source, stdin: stdin) + DispatchQueue.main.async { done(result.0, result.1) } + } + #else + done("unavailable", "") + #endif + } + + #if os(macOS) + private static func runMac(_ source: String, stdin: String) -> (String, String) { + guard source.count <= 100_000 else { return ("fail", "Program je příliš dlouhý.") } + guard let dotnet = findDotnet() else { return ("unavailable", "") } + let dir = URL(fileURLWithPath: NSHomeDirectory()).appendingPathComponent(".graduately/csharp-lab") + let csproj = dir.appendingPathComponent("Lab.csproj") + if !FileManager.default.fileExists(atPath: csproj.path) { + try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) + let framework = targetFramework(dotnet) + let xml = """ + + + Exe + \(framework) + enable + disable + latest + + + """ + try? xml.write(to: csproj, atomically: true, encoding: .utf8) + let restored = command(dotnet, ["restore", dir.path], dir, "", 90) + if restored.0 != "ok" { return restored } + } + do { + try source.write(to: dir.appendingPathComponent("Program.cs"), atomically: true, encoding: .utf8) + } catch { + return ("fail", error.localizedDescription) + } + var args = ["run", "--project", dir.path, "--verbosity", "quiet"] + if FileManager.default.fileExists(atPath: dir.appendingPathComponent("obj").path) { + args.append("--no-restore") + } + return command(dotnet, args, dir, stdin, 20) + } + + private static func command(_ dotnet: String, _ args: [String], _ dir: URL, _ stdin: String, _ seconds: Double) -> (String, String) { + let proc = Process() + proc.executableURL = URL(fileURLWithPath: dotnet) + proc.arguments = args + proc.currentDirectoryURL = dir + var env = ProcessInfo.processInfo.environment + let root = (dotnet as NSString).deletingLastPathComponent + env["PATH"] = [root, "/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin"].joined(separator: ":") + env["DOTNET_ROOT"] = root + env["DOTNET_CLI_TELEMETRY_OPTOUT"] = "1" + env["DOTNET_NOLOGO"] = "1" + env["DOTNET_SKIP_FIRST_TIME_EXPERIENCE"] = "1" + proc.environment = env + let out = Pipe() + let input = Pipe() + proc.standardOutput = out + proc.standardError = out + proc.standardInput = input + var collected = Data() + let lock = NSLock() + out.fileHandleForReading.readabilityHandler = { handle in + let chunk = handle.availableData + guard !chunk.isEmpty else { return } + lock.lock() + collected.append(chunk) + lock.unlock() + } + do { try proc.run() } catch { return ("fail", error.localizedDescription) } + if let data = stdin.data(using: .utf8) { input.fileHandleForWriting.write(data) } + try? input.fileHandleForWriting.close() + let group = DispatchGroup() + group.enter() + proc.terminationHandler = { _ in group.leave() } + if group.wait(timeout: .now() + seconds) == .timedOut { + proc.terminate() + kill(proc.processIdentifier, SIGKILL) + out.fileHandleForReading.readabilityHandler = nil + return ("timeout", clip(collected, lock)) + } + out.fileHandleForReading.readabilityHandler = nil + let rest = out.fileHandleForReading.readDataToEndOfFile() + lock.lock() + collected.append(rest) + lock.unlock() + let text = clip(collected, lock) + return proc.terminationStatus == 0 ? ("ok", text) : ("fail", text) + } + + private static func clip(_ data: Data, _ lock: NSLock) -> String { + lock.lock() + let copy = data + lock.unlock() + let text = String(data: copy, encoding: .utf8) ?? "" + let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines) + return trimmed.count <= 16_000 ? trimmed : String(trimmed.prefix(16_000)) + "\n…" + } + + private static func targetFramework(_ dotnet: String) -> String { + let version = command(dotnet, ["--version"], URL(fileURLWithPath: NSTemporaryDirectory()), "", 20) + let major = version.1.split(separator: ".").first.map(String.init) ?? "" + return major.allSatisfy(\.isNumber) && !major.isEmpty ? "net\(major).0" : "net8.0" + } + + private static func findDotnet() -> String? { + let names = [ + "/usr/local/share/dotnet/dotnet", + "/opt/homebrew/share/dotnet/dotnet", + "/usr/local/bin/dotnet", + NSHomeDirectory() + "/.dotnet/dotnet", + ] + return names.first { FileManager.default.isExecutableFile(atPath: $0) } + } + #endif +} diff --git a/scripts/build-content.py b/scripts/build-content.py index d6685cb..efcd131 100644 --- a/scripts/build-content.py +++ b/scripts/build-content.py @@ -116,9 +116,20 @@ def parse_doc(path: Path) -> Doc: para: Node | None = None table: Node | None = None in_comment = False - for n in range(i, len(lines)): + indexes = iter(range(i, len(lines))) + for n in indexes: ln = lines[n].rstrip() no = n + 1 + if ln.startswith("```"): + para = table = None + body: list[str] = [] + for n2 in indexes: + raw_line = lines[n2] + if raw_line.rstrip().startswith("```"): + break + body.append(raw_line) + nodes.append(Node("code", "\n".join(body).strip("\n"), no)) + continue if in_comment: if "-->" in ln: in_comment = False @@ -539,6 +550,36 @@ def lesson_files(directory: Path) -> list[Path]: return files +def code_of(p: Part | None) -> str: + if p is None: + return "" + for nd in p.nodes: + if nd.kind == "code": + return nd.text + return "" + + +def code_lessons(directory: Path, prefix: str) -> list[dict]: + """Lessons with an explanation and a C# program the student can edit and run.""" + out = [] + for i, path in enumerate(lesson_files(directory), start=1): + doc, en = parse_doc(path), en_doc(path) + load_translations(en) + lid = lesson_id(doc, f"{prefix}.{i}") + code = code_of(part(doc, "Kód", "Code")) + if not code.strip(): + fail(path, None, "lekce nemá ukázkový kód v sekci # Kód") + out.append({ + "id": i, + "key": lid, + "titleKey": keyed(f"{prefix}_unit{i}", doc, en, "nazev"), + "subKey": keyed(f"{prefix}_unit{i}_sub", doc, en, "popis"), + "slides": read_slides(doc, part(doc, "Výklad")), + "code": code, + }) + return out + + def std_lessons(directory: Path, prefix: str) -> list[dict]: """Lessons with slides and a choice quiz (IT, civics, literature, sciences, physics).""" out = [] @@ -1068,6 +1109,10 @@ STD = [ ("technicka-grafika/1-rocnik", "tg"), ("technicka-grafika/2-rocnik", "tg2"), ("technicka-grafika/3-rocnik", "tg3"), ("technicka-grafika/4-rocnik", "tg4"), ] +CODE = [ + ("programovani/1-rocnik", "cs"), ("programovani/2-rocnik", "cs2"), + ("programovani/3-rocnik", "cs3"), ("programovani/4-rocnik", "cs4"), +] MATH = [ ("matematika/0-opakovani", "mat0"), ("matematika/1-rocnik", "mat"), ("matematika/2-rocnik", "mat2"), ("matematika/3-rocnik", "mat3"), @@ -1091,6 +1136,8 @@ def build(content: Path = CONTENT) -> dict: payload[key] = std_lessons(content / d, key) for d, key in MATH: payload[key] = math_lessons(content / d, key) + for d, key in CODE: + payload[key] = code_lessons(content / d, key) for d, tag in SKILLS: for year in range(1, 5): payload[tag if year == 1 else f"{tag}{year}"] = skill_lessons( From 0047bd74f0362720a5bf41b0c37bb4db6c894c2b Mon Sep 17 00:00:00 2001 From: pepaskopec-blip Date: Sun, 11 Oct 2026 16:31:12 +0200 Subject: [PATCH 4/7] Polish the UI on all platforms with progress and a two-column code editor. Co-authored-by: Cursor --- .../org/maturita/maturita/desktop/Main.kt | 2 + .../org/maturita/maturita/ui/CodeLesson.kt | 305 +++++-- .../org/maturita/maturita/ui/Components.kt | 182 ++++- .../src/org/maturita/maturita/ui/Screens.kt | 333 +++++--- content/aplikace/texty.md | 3 + ios/Graduately/UI/Components.swift | 268 ++++--- ios/Graduately/UI/Exercises.swift | 18 +- ios/Graduately/UI/PathMap.swift | 19 + ios/Graduately/UI/Screens.swift | 758 ++++++++++++------ 9 files changed, 1291 insertions(+), 597 deletions(-) diff --git a/android/desktop/src/main/kotlin/org/maturita/maturita/desktop/Main.kt b/android/desktop/src/main/kotlin/org/maturita/maturita/desktop/Main.kt index 98d77e9..aa9a704 100644 --- a/android/desktop/src/main/kotlin/org/maturita/maturita/desktop/Main.kt +++ b/android/desktop/src/main/kotlin/org/maturita/maturita/desktop/Main.kt @@ -40,6 +40,8 @@ fun main() { val info = Properties().apply { resourceText("build-info.properties")?.let { load(it.reader()) } } val platform = DesktopPlatform(info.getProperty("commit") ?: "dev", prefs, install) val vm = AppViewModel(Content.parse(resourceText("content.json")), platform) + // `GRADUATELY_PAGE=u1e3 make run` opens a page, for screenshots. + System.getenv("GRADUATELY_PAGE")?.takeIf { it.isNotBlank() }?.let { vm.openPage(it) } val icon = Thread.currentThread().contextClassLoader.getResourceAsStream("icon.png") ?.use { ImageIO.read(it) }?.let { BitmapPainter(it.toComposeImageBitmap()) } diff --git a/android/shared/src/org/maturita/maturita/ui/CodeLesson.kt b/android/shared/src/org/maturita/maturita/ui/CodeLesson.kt index c51b2ce..3e8640e 100644 --- a/android/shared/src/org/maturita/maturita/ui/CodeLesson.kt +++ b/android/shared/src/org/maturita/maturita/ui/CodeLesson.kt @@ -1,32 +1,59 @@ package org.maturita.maturita.ui -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.FilledTonalButton +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Cancel +import androidx.compose.material.icons.filled.CheckCircle +import androidx.compose.material.icons.filled.Code +import androidx.compose.material.icons.filled.PlayArrow +import androidx.compose.material.icons.filled.Refresh +import androidx.compose.material.icons.filled.Terminal +import androidx.compose.material3.Button +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.luminance +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.input.OffsetMapping +import androidx.compose.ui.text.input.TransformedText +import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.max +import androidx.compose.ui.unit.min import androidx.compose.ui.unit.sp import org.maturita.maturita.AppViewModel import org.maturita.maturita.data.J @@ -34,18 +61,21 @@ import org.maturita.maturita.data.J /** Explanation on one side, the program and its output on the other. */ @Composable fun CodeLessonScreen(vm: AppViewModel, lesson: J, onSuccess: () -> Unit) { + val key = lesson.str("key") val starter = lesson.str("code") - var code by remember(lesson.str("key")) { mutableStateOf(starter) } - var stdin by remember(lesson.str("key")) { mutableStateOf("") } - var output by remember(lesson.str("key")) { mutableStateOf("") } - var running by remember(lesson.str("key")) { mutableStateOf(false) } + var code by remember(key) { mutableStateOf(starter) } + var stdin by remember(key) { mutableStateOf("") } + var output by remember(key) { mutableStateOf("") } + var status by remember(key) { mutableStateOf("") } + val running = status == "running" + val needsInput = "ReadLine" in code fun run() { if (running) return - running = true + status = "running" output = vm.tr("cs_running") - vm.platform.runCSharp(code, stdin) { result -> - running = false + vm.platform.runCSharp(code, if (needsInput) stdin else "") { result -> + status = result.status output = when (result.status) { "unavailable" -> vm.tr("cs_unavailable") "timeout" -> (result.output + "\n\n" + vm.tr("cs_timeout")).trim() @@ -55,19 +85,39 @@ fun CodeLessonScreen(vm: AppViewModel, lesson: J, onSuccess: () -> Unit) { } } - DetailScaffold(vm, vm.tr(lesson.str("titleKey")), vm.tr(lesson.str("subKey"))) { inner -> - BoxWithConstraints(Modifier.fillMaxSize().padding(inner).padding(12.dp)) { - if (maxWidth >= 840.dp) { - Row(Modifier.fillMaxSize(), horizontalArrangement = Arrangement.spacedBy(12.dp)) { - Explanation(vm, lesson, Modifier.weight(0.9f).fillMaxHeight()) - EditorColumn(vm, code, { code = it }, stdin, { stdin = it }, expand = true, Modifier.weight(1.15f).fillMaxHeight()) - OutputColumn(vm, output, running, { run() }, { code = starter }, expand = true, Modifier.weight(1f).fillMaxHeight()) + fun reset() { + code = starter + output = "" + status = "" + } + + DetailScaffold(vm, vm.tr(lesson.str("titleKey")), vm.tr(lesson.str("subKey")), maxContentWidth = 1640.dp) { inner -> + BoxWithConstraints(Modifier.fillMaxSize().padding(inner)) { + val width = maxWidth + val height = maxHeight + if (width >= 900.dp) { + Row( + Modifier.fillMaxSize().padding(horizontal = 24.dp, vertical = 16.dp), + horizontalArrangement = Arrangement.spacedBy(24.dp), + ) { + Column(Modifier.width(min(560.dp, width * 0.4f)).fillMaxHeight().verticalScroll(rememberScrollState())) { + Explanation(lesson) + } + Column(Modifier.weight(1f).fillMaxHeight(), verticalArrangement = Arrangement.spacedBy(16.dp)) { + EditorCard(vm, code, { code = it }, running, ::run, ::reset, compact = false, Modifier.weight(1f)) + if (needsInput) InputField(vm, stdin) { stdin = it } + OutputCard(vm, output, status, Modifier.height(max(180.dp, height * 0.32f))) + } } } else { - Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.spacedBy(12.dp)) { - Explanation(vm, lesson, Modifier.fillMaxWidth()) - EditorColumn(vm, code, { code = it }, stdin, { stdin = it }, expand = false, Modifier.fillMaxWidth()) - OutputColumn(vm, output, running, { run() }, { code = starter }, expand = false, Modifier.fillMaxWidth()) + Column( + Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + ) { + Explanation(lesson) + EditorCard(vm, code, { code = it }, running, ::run, ::reset, compact = true, Modifier.height(320.dp)) + if (needsInput) InputField(vm, stdin) { stdin = it } + OutputCard(vm, output, status, Modifier.height(220.dp)) } } } @@ -75,84 +125,179 @@ fun CodeLessonScreen(vm: AppViewModel, lesson: J, onSuccess: () -> Unit) { } @Composable -private fun Explanation(vm: AppViewModel, lesson: J, modifier: Modifier) { - Column(modifier.verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.spacedBy(14.dp)) { - lesson.arr("slides").forEach { slide -> - Text(slide.str("kicker"), color = MaterialTheme.colorScheme.primary, style = MaterialTheme.typography.labelLarge) - Text(slide.str("title"), style = MaterialTheme.typography.titleLarge) - slide.strs("lines").forEach { line -> - Text("• $line", style = MaterialTheme.typography.bodyLarge) - } - slide.strOrNull("tip")?.let { tip -> - Text(tip, color = MaterialTheme.colorScheme.onSurfaceVariant, style = MaterialTheme.typography.bodyMedium) - } - } +private fun Explanation(lesson: J) { + Column(verticalArrangement = Arrangement.spacedBy(28.dp)) { + lesson.arr("slides").forEach { SlideContent(it) } } } @Composable -private fun EditorColumn( +private fun EditorCard( vm: AppViewModel, code: String, onCode: (String) -> Unit, - stdin: String, - onStdin: (String) -> Unit, - expand: Boolean, + running: Boolean, + onRun: () -> Unit, + onReset: () -> Unit, + compact: Boolean, modifier: Modifier, ) { - Column(modifier, verticalArrangement = Arrangement.spacedBy(8.dp)) { - Text(vm.tr("cs_code"), style = MaterialTheme.typography.titleMedium) - BasicTextField( - value = code, - onValueChange = onCode, - textStyle = codeStyle().copy(color = MaterialTheme.colorScheme.onSurface), - cursorBrush = SolidColor(MaterialTheme.colorScheme.primary), - modifier = Modifier - .then(if (expand) Modifier.weight(1f) else Modifier.height(240.dp)) - .fillMaxWidth() - .background(MaterialTheme.colorScheme.surfaceContainer) - .padding(12.dp) - .verticalScroll(rememberScrollState()), - ) + val dark = MaterialTheme.colorScheme.background.luminance() < 0.5f + val highlight = remember(dark) { CSharpHighlight(dark) } + Surface(modifier.fillMaxWidth(), shape = RoundedCornerShape(16.dp), color = MaterialTheme.colorScheme.surfaceContainerHigh) { + Column { + Row( + Modifier.fillMaxWidth().padding(horizontal = 14.dp, vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(10.dp), + ) { + Icon(Icons.Filled.Code, null, tint = MaterialTheme.colorScheme.primary) + Text("Program.cs", style = MaterialTheme.typography.titleSmall.copy(fontFamily = FontFamily.Monospace)) + Spacer(Modifier.weight(1f)) + if (compact) { + IconButton(onClick = onReset, enabled = !running) { + Icon(Icons.Filled.Refresh, contentDescription = vm.tr("cs_reset")) + } + } else { + OutlinedButton(onClick = onReset, enabled = !running) { + Icon(Icons.Filled.Refresh, null, Modifier.size(18.dp)) + Spacer(Modifier.width(6.dp)) + Text(vm.tr("cs_reset")) + } + } + Button(onClick = onRun, enabled = !running) { + Icon(Icons.Filled.PlayArrow, null, Modifier.size(18.dp)) + Spacer(Modifier.width(6.dp)) + Text(vm.tr("cs_run")) + } + } + HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant) + BasicTextField( + value = code, + onValueChange = onCode, + textStyle = codeStyle().copy(color = MaterialTheme.colorScheme.onSurface), + cursorBrush = SolidColor(MaterialTheme.colorScheme.primary), + visualTransformation = highlight, + modifier = Modifier.fillMaxSize().padding(14.dp), + ) + } + } +} + +@Composable +private fun InputField(vm: AppViewModel, stdin: String, onStdin: (String) -> Unit) { + Column(verticalArrangement = Arrangement.spacedBy(6.dp)) { Text(vm.tr("cs_stdin"), style = MaterialTheme.typography.labelLarge, color = MaterialTheme.colorScheme.onSurfaceVariant) - BasicTextField( + OutlinedTextField( value = stdin, onValueChange = onStdin, - textStyle = codeStyle().copy(color = MaterialTheme.colorScheme.onSurface), - cursorBrush = SolidColor(MaterialTheme.colorScheme.primary), - modifier = Modifier.fillMaxWidth().background(MaterialTheme.colorScheme.surfaceContainer).padding(12.dp), + textStyle = codeStyle(), + minLines = 1, + maxLines = 4, + modifier = Modifier.fillMaxWidth(), ) } } @Composable -private fun OutputColumn( - vm: AppViewModel, - output: String, - running: Boolean, - onRun: () -> Unit, - onReset: () -> Unit, - expand: Boolean, - modifier: Modifier, -) { - Column(modifier, verticalArrangement = Arrangement.spacedBy(8.dp)) { - Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { - FilledTonalButton(onClick = onRun, enabled = !running) { Text(vm.tr("cs_run")) } - OutlinedButton(onClick = onReset, enabled = !running) { Text(vm.tr("cs_reset")) } +private fun OutputCard(vm: AppViewModel, output: String, status: String, modifier: Modifier) { + val muted = output.isEmpty() || status == "running" + Surface(modifier.fillMaxWidth(), shape = RoundedCornerShape(16.dp), color = MaterialTheme.colorScheme.surfaceContainerHigh) { + Column { + Row( + Modifier.fillMaxWidth().height(48.dp).padding(horizontal = 14.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon(Icons.Filled.Terminal, null, tint = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.size(18.dp)) + Spacer(Modifier.width(8.dp)) + Text(vm.tr("cs_output"), style = MaterialTheme.typography.titleSmall) + Spacer(Modifier.weight(1f)) + StatusBadge(vm, status) + } + HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant) + Text( + output.ifEmpty { vm.tr("cs_empty") }, + style = codeStyle(), + color = if (muted) MaterialTheme.colorScheme.onSurfaceVariant else MaterialTheme.colorScheme.onSurface, + modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(14.dp), + ) } - Text(vm.tr("cs_output"), style = MaterialTheme.typography.titleMedium) - Text( - output.ifEmpty { vm.tr("cs_empty") }, - style = codeStyle(), - color = MaterialTheme.colorScheme.onSurface, - modifier = Modifier - .then(if (expand) Modifier.weight(1f) else Modifier.height(180.dp)) - .fillMaxWidth() - .background(MaterialTheme.colorScheme.surfaceContainer) - .padding(12.dp) - .verticalScroll(rememberScrollState()), - ) } } -private fun codeStyle() = TextStyle(fontFamily = FontFamily.Monospace, fontSize = 14.sp, lineHeight = 20.sp) +@Composable +private fun StatusBadge(vm: AppViewModel, status: String) { + when (status) { + "running" -> CircularProgressIndicator(Modifier.size(18.dp), strokeWidth = 2.dp) + "ok" -> Badge(vm.tr("cs_done"), Icons.Filled.CheckCircle, MaterialTheme.colorScheme.primary) + "fail", "timeout" -> Badge(vm.tr("cs_failed"), Icons.Filled.Cancel, MaterialTheme.colorScheme.error) + } +} + +@Composable +private fun Badge(text: String, icon: androidx.compose.ui.graphics.vector.ImageVector, color: Color) { + Row(verticalAlignment = Alignment.CenterVertically) { + Icon(icon, null, tint = color, modifier = Modifier.size(18.dp)) + Spacer(Modifier.width(6.dp)) + Text(text, style = MaterialTheme.typography.labelLarge, color = color) + } +} + +private fun codeStyle() = TextStyle(fontFamily = FontFamily.Monospace, fontSize = 15.sp, lineHeight = 22.sp) + +/** Colours C# keywords, types, numbers, strings and comments without changing the text. */ +private class CSharpHighlight(dark: Boolean) : VisualTransformation { + private val palette = listOf( + Color(if (dark) 0xFFFF7AB2 else 0xFF9B2393), + Color(if (dark) 0xFF5DD8FF else 0xFF0B4F79), + Color(if (dark) 0xFFD9C97C else 0xFF1C00CF), + Color(if (dark) 0xFFFC6A5D else 0xFFC41A16), + Color(if (dark) 0xFF7F8C98 else 0xFF5D6C79), + ) + + override fun filter(text: AnnotatedString): TransformedText = + TransformedText(highlight(text.text), OffsetMapping.Identity) + + private fun highlight(code: String): AnnotatedString { + val kind = IntArray(code.length) { -1 } + RULES.forEachIndexed { index, rule -> + rule.findAll(code).forEach { m -> for (i in m.range) kind[i] = index } + } + return buildAnnotatedString { + var start = 0 + while (start < code.length) { + var end = start + while (end < code.length && kind[end] == kind[start]) end++ + val k = kind[start] + if (k >= 0) { + pushStyle(SpanStyle(color = palette[k])) + append(code, start, end) + pop() + } else { + append(code, start, end) + } + start = end + } + } + } + + companion object { + private val KEYWORDS = listOf( + "abstract", "as", "async", "await", "base", "bool", "break", "case", "catch", "char", "class", + "const", "continue", "decimal", "default", "do", "double", "else", "enum", "false", "finally", + "float", "for", "foreach", "get", "if", "in", "int", "interface", "internal", "is", "long", + "namespace", "new", "null", "object", "out", "override", "private", "protected", "public", + "readonly", "record", "ref", "return", "sealed", "set", "static", "string", "struct", "switch", + "this", "throw", "true", "try", "using", "var", "virtual", "void", "while", + ) + + /** Later rules win, so strings and comments override keywords inside them. */ + private val RULES = listOf( + Regex("\\b(?:" + KEYWORDS.joinToString("|") + ")\\b"), + Regex("\\b(?:Console|Math|List|Dictionary|File|Task|Exception|Func|Action|String|DateTime|Random)\\b"), + Regex("\\b\\d+(?:\\.\\d+)?\\b"), + Regex("\\$?@?\"(?:\\\\.|[^\"\\\\\\n])*\"|'(?:\\\\.|[^'\\\\\\n])'"), + Regex("//[^\\n]*|/\\*[\\s\\S]*?\\*/"), + ) + } +} diff --git a/android/shared/src/org/maturita/maturita/ui/Components.kt b/android/shared/src/org/maturita/maturita/ui/Components.kt index 6798891..6bdde68 100644 --- a/android/shared/src/org/maturita/maturita/ui/Components.kt +++ b/android/shared/src/org/maturita/maturita/ui/Components.kt @@ -8,15 +8,22 @@ import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.shrinkVertically import androidx.compose.animation.animateColorAsState +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn @@ -24,7 +31,17 @@ import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.windowInsetsPadding +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.ui.draw.clip +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.unit.Dp import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.text.KeyboardOptions @@ -44,7 +61,6 @@ import androidx.compose.material3.FilterChip import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.LargeTopAppBar -import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.ListItem import androidx.compose.material3.ListItemDefaults import androidx.compose.material3.MaterialTheme @@ -150,7 +166,19 @@ fun AppNavBar(vm: AppViewModel) { private fun navIconColor(selected: Boolean): Color = if (selected) MaterialTheme.colorScheme.onSecondaryContainer else MaterialTheme.colorScheme.onSurfaceVariant -/** Tab root: large collapsing title, search action, bottom navigation. */ +/** Lists and exercises stop growing here so they keep their shape in wide desktop windows. */ +val ContentMaxWidth = 880.dp +val ActionMaxWidth = 560.dp + +/** Caps the width of [content] and centres it; phones are narrower than the cap anyway. */ +@Composable +fun CenteredColumn(maxWidth: Dp = ContentMaxWidth, content: @Composable () -> Unit) { + Box(Modifier.fillMaxSize(), contentAlignment = Alignment.TopCenter) { + Box(Modifier.widthIn(max = maxWidth).fillMaxSize()) { content() } + } +} + +/** Tab root: large collapsing title on phones, a compact one in wide windows. */ @OptIn(ExperimentalMaterial3Api::class) @Composable fun RootScaffold( @@ -159,30 +187,48 @@ fun RootScaffold( showSearch: Boolean = true, content: LazyListScope.() -> Unit, ) { - val scroll = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()) - Scaffold( - modifier = Modifier.nestedScroll(scroll.nestedScrollConnection), - topBar = { - LargeTopAppBar( - title = { Text(title, maxLines = 1, overflow = TextOverflow.Ellipsis) }, - actions = { - if (showSearch) { - IconButton(onClick = { vm.searchOpen = true }) { - Icon(Icons.Filled.Search, contentDescription = vm.tr("search_placeholder")) - } - } - }, - scrollBehavior = scroll, - ) - }, - bottomBar = { AppNavBar(vm) }, - ) { inner -> - LazyColumn( - contentPadding = PaddingValues( - top = inner.calculateTopPadding(), - bottom = inner.calculateBottomPadding() + 16.dp, - ), - ) { content() } + BoxWithConstraints { + val wide = maxWidth >= 600.dp + val scroll = if (wide) { + TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) + } else { + TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()) + } + val actions: @Composable RowScope.() -> Unit = { + if (showSearch) { + IconButton(onClick = { vm.searchOpen = true }) { + Icon(Icons.Filled.Search, contentDescription = vm.tr("search_placeholder")) + } + } + } + Scaffold( + modifier = Modifier.nestedScroll(scroll.nestedScrollConnection), + topBar = { + if (wide) { + TopAppBar( + title = { Text(title, maxLines = 1, overflow = TextOverflow.Ellipsis, style = MaterialTheme.typography.headlineSmall) }, + actions = actions, + scrollBehavior = scroll, + ) + } else { + LargeTopAppBar( + title = { Text(title, maxLines = 1, overflow = TextOverflow.Ellipsis) }, + actions = actions, + scrollBehavior = scroll, + ) + } + }, + bottomBar = { AppNavBar(vm) }, + ) { inner -> + CenteredColumn { + LazyColumn( + contentPadding = PaddingValues( + top = inner.calculateTopPadding(), + bottom = inner.calculateBottomPadding() + 16.dp, + ), + ) { content() } + } + } } } @@ -194,6 +240,7 @@ fun DetailScaffold( title: String, subtitle: String? = null, bottomBar: (@Composable () -> Unit)? = null, + maxContentWidth: Dp = ContentMaxWidth, content: @Composable (PaddingValues) -> Unit, ) { val scroll = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) @@ -224,8 +271,9 @@ fun DetailScaffold( ) }, bottomBar = { bottomBar?.invoke() }, - content = content, - ) + ) { inner -> + CenteredColumn(maxContentWidth) { content(inner) } + } } /** Feedback line plus the primary action, anchored at the bottom of an exercise. */ @@ -242,6 +290,8 @@ fun ActionBar( Column( Modifier .fillMaxWidth() + .wrapContentWidth(Alignment.CenterHorizontally) + .widthIn(max = ActionMaxWidth) .windowInsetsPadding(WindowInsets.navigationBars) .padding(horizontal = 16.dp, vertical = 12.dp), ) { @@ -276,6 +326,27 @@ fun SectionHeader(text: String) { ) } +/** Number or short label in a tinted square, so rows line up whatever the label's width. */ +@Composable +fun LeadingBadge(text: String) { + Box( + Modifier.size(36.dp).clip(RoundedCornerShape(10.dp)).background(MaterialTheme.colorScheme.primary.copy(alpha = 0.14f)), + contentAlignment = Alignment.Center, + ) { + Text(text, style = MaterialTheme.typography.titleSmall, color = MaterialTheme.colorScheme.primary) + } +} + +@Composable +fun LeadingIcon(icon: ImageVector) { + Box( + Modifier.size(36.dp).clip(RoundedCornerShape(10.dp)).background(MaterialTheme.colorScheme.primary.copy(alpha = 0.14f)), + contentAlignment = Alignment.Center, + ) { + Icon(icon, null, tint = MaterialTheme.colorScheme.primary, modifier = Modifier.size(20.dp)) + } +} + /** One navigable row: done/pending/locked state on the left, chevron on the right. */ @Composable fun NavRow( @@ -284,15 +355,33 @@ fun NavRow( done: Boolean = false, locked: Boolean = false, leading: (@Composable () -> Unit)? = null, + progress: Pair? = null, onClick: (() -> Unit)?, ) { val enabled = !locked && onClick != null val textColor = if (enabled) MaterialTheme.colorScheme.onSurface else MaterialTheme.colorScheme.onSurfaceVariant val rowModifier = if (enabled) Modifier.clickable(onClick = onClick!!) else Modifier + val shownSubtitle = subtitle?.takeIf { it.isNotBlank() } + val shownProgress = progress?.takeIf { it.second > 0 } ListItem( headlineContent = { Text(title, color = textColor) }, - supportingContent = subtitle?.takeIf { it.isNotBlank() }?.let { - { Text(it, color = MaterialTheme.colorScheme.onSurfaceVariant) } + supportingContent = if (shownSubtitle == null && shownProgress == null) null else { + { + Column { + shownSubtitle?.let { Text(it, color = MaterialTheme.colorScheme.onSurfaceVariant) } + shownProgress?.let { (doneCount, total) -> + Row(Modifier.padding(top = 8.dp), verticalAlignment = Alignment.CenterVertically) { + SmoothProgress(doneCount / total.toFloat(), Modifier.weight(1f).height(6.dp)) + Spacer(Modifier.width(10.dp)) + Text( + "$doneCount / $total", + style = MaterialTheme.typography.labelMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + } + } }, leadingContent = { when { @@ -328,19 +417,38 @@ fun SmoothProgress( progress: Float, modifier: Modifier = Modifier, color: Color = MaterialTheme.colorScheme.primary, - trackColor: Color = MaterialTheme.colorScheme.surfaceVariant, + trackColor: Color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.1f), ) { val animated by animateFloatAsState( targetValue = progress.coerceIn(0f, 1f), animationSpec = tween(durationMillis = 380), label = "progress", ) - LinearProgressIndicator( - progress = { animated }, - modifier = modifier, - color = color, - trackColor = trackColor, - ) + Box(modifier.heightIn(min = 4.dp).clip(CircleShape).background(trackColor)) { + if (animated > 0f) { + Box(Modifier.fillMaxHeight().fillMaxWidth(animated).clip(CircleShape).background(color)) + } + } +} + +/** Circular progress with a label in the middle. */ +@Composable +fun ProgressRing(progress: Float, modifier: Modifier = Modifier, stroke: Dp = 10.dp, label: @Composable () -> Unit) { + val animated by animateFloatAsState(progress.coerceIn(0f, 1f), tween(durationMillis = 380), label = "ring") + val track = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.1f) + val fill = MaterialTheme.colorScheme.primary + Box(modifier, contentAlignment = Alignment.Center) { + Canvas(Modifier.fillMaxSize()) { + val width = stroke.toPx() + val inset = width / 2 + val arcSize = Size(size.width - width, size.height - width) + drawArc(track, 0f, 360f, false, Offset(inset, inset), arcSize, style = Stroke(width)) + if (animated > 0f) { + drawArc(fill, -90f, 360f * animated, false, Offset(inset, inset), arcSize, style = Stroke(width, cap = StrokeCap.Round)) + } + } + label() + } } @Composable diff --git a/android/shared/src/org/maturita/maturita/ui/Screens.kt b/android/shared/src/org/maturita/maturita/ui/Screens.kt index 761a024..39404c3 100644 --- a/android/shared/src/org/maturita/maturita/ui/Screens.kt +++ b/android/shared/src/org/maturita/maturita/ui/Screens.kt @@ -26,7 +26,10 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.MenuBook import androidx.compose.material.icons.filled.Architecture +import androidx.compose.material.icons.filled.AutoAwesome +import androidx.compose.material.icons.filled.AutoStories import androidx.compose.material.icons.filled.Bolt +import androidx.compose.material.icons.filled.Eco import androidx.compose.material.icons.filled.Functions import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Close @@ -101,50 +104,61 @@ fun HomeScreen(vm: AppViewModel) { item { Card( modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 8.dp), - colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.primaryContainer), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceContainerHigh, + contentColor = MaterialTheme.colorScheme.onSurface, + ), ) { - Column(Modifier.padding(20.dp)) { - Text( - vm.tr("welcome_title"), - style = MaterialTheme.typography.headlineMedium, - color = MaterialTheme.colorScheme.onPrimaryContainer, - ) - Spacer(Modifier.height(8.dp)) - Text( - vm.tr(vm.platform.welcomeKey), - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.8f), - ) - Spacer(Modifier.height(16.dp)) - SmoothProgress( - progress = pct, - modifier = Modifier.fillMaxWidth().height(6.dp).clip(CircleShape), - color = MaterialTheme.colorScheme.onPrimaryContainer, - trackColor = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.18f), - ) - Spacer(Modifier.height(6.dp)) - Text( - vm.fmt("stats_ex_fmt", sum.doneEx, sum.totalEx), - style = MaterialTheme.typography.labelMedium, - color = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.8f), - ) + Row(Modifier.padding(20.dp), verticalAlignment = Alignment.CenterVertically) { + Column(Modifier.weight(1f)) { + Text(vm.tr("welcome_title"), style = MaterialTheme.typography.headlineMedium) + Spacer(Modifier.height(8.dp)) + Text( + vm.tr(vm.platform.welcomeKey), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + Spacer(Modifier.width(16.dp)) + ProgressRing(pct, Modifier.size(104.dp)) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Text(vm.fmt("stats_pct_fmt", (pct * 100).toInt()), style = MaterialTheme.typography.titleLarge) + Text( + vm.fmt("stats_ex_fmt", sum.doneEx, sum.totalEx), + style = MaterialTheme.typography.labelSmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } } } } if (vm.update.canInstall || vm.update.status == "downloading" || vm.update.status == "staged") { item { UpdateBanner(vm) } } + val open = vm.content.subjects.filter { it.bool("open") } + val soon = vm.content.subjects.filter { !it.bool("open") } item { SectionHeader(vm.tr("subjects_title")) } - items(vm.content.subjects) { s -> - val open = s.bool("open") + items(open) { s -> + val part = subjectSum(vm, s) NavRow( title = vm.tr(s.str("key")), - subtitle = if (open) null else vm.tr("stats_locked"), - locked = !open, - leading = { SubjectIcon(s.str("icon"), open) }, - onClick = if (open) ({ vm.goPage(s.str("target")) }) else null, + leading = { SubjectIcon(s.str("icon"), true) }, + progress = part.doneEx to part.totalEx, + onClick = { vm.goPage(s.str("target")) }, ) } + if (soon.isNotEmpty()) { + item { SectionHeader(vm.tr("subjects_soon")) } + items(soon) { s -> + NavRow( + title = vm.tr(s.str("key")), + locked = true, + leading = { SubjectIcon(s.str("icon"), false) }, + onClick = null, + ) + } + } } } @@ -154,14 +168,18 @@ private fun ChangelogCard(vm: AppViewModel) { val blocks = vm.content.changelog.take(3) Card( modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 8.dp), - colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.tertiaryContainer), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceContainerHigh, + contentColor = MaterialTheme.colorScheme.onSurface, + ), ) { Column(Modifier.padding(20.dp)) { Row(verticalAlignment = Alignment.CenterVertically) { + Icon(Icons.Filled.AutoAwesome, null, tint = MaterialTheme.colorScheme.primary, modifier = Modifier.size(20.dp)) + Spacer(Modifier.width(8.dp)) Text( vm.tr("changelog_title"), style = MaterialTheme.typography.titleMedium, - color = MaterialTheme.colorScheme.onTertiaryContainer, modifier = Modifier.weight(1f), ) val date = blocks.firstOrNull()?.str("date").orEmpty() @@ -169,7 +187,7 @@ private fun ChangelogCard(vm: AppViewModel) { Text( date, style = MaterialTheme.typography.labelMedium, - color = MaterialTheme.colorScheme.onTertiaryContainer.copy(alpha = 0.7f), + color = MaterialTheme.colorScheme.onSurfaceVariant, ) } } @@ -180,7 +198,6 @@ private fun ChangelogCard(vm: AppViewModel) { Text( "• $line", style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onTertiaryContainer, modifier = Modifier.padding(bottom = 4.dp), ) } @@ -293,6 +310,67 @@ private fun Metric(label: String, value: String, modifier: Modifier) { } } +/** Done and total exercises behind one row of a years screen, or null when there is no count. */ +fun routeProgress(vm: AppViewModel, r: Route): Pair? { + val c = vm.content + val p = vm.progress + fun tally(lessons: List, done: (Int) -> Boolean) = lessons.count { done(it.int("id")) } to lessons.size + return when (r) { + Route.NetMap -> tally(c.netLessons, p::netDone) + Route.Net2Map -> tally(c.itLessons("net2")) { p.courseDone("net2", it) } + Route.Net3Map -> tally(c.itLessons("net3")) { p.courseDone("net3", it) } + Route.Net4Map -> tally(c.itLessons("net4")) { p.courseDone("net4", it) } + Route.HwMap -> tally(c.hw, p::hwDone) + Route.Hw2Map -> tally(c.itLessons("hw2")) { p.courseDone("hw2", it) } + Route.Hw3Map -> tally(c.itLessons("hw3")) { p.courseDone("hw3", it) } + Route.Hw4Map -> tally(c.itLessons("hw4")) { p.courseDone("hw4", it) } + Route.OnMap -> tally(c.on, p::onDone) + Route.On2Map -> tally(c.on2, p::on2Done) + Route.On3Map -> tally(c.on3, p::on3Done) + Route.On4Map -> tally(c.on4, p::on4Done) + is Route.EnMap -> tally(c.enYear(r.year)) { p.enDone(r.year, it) } + is Route.DeMap -> tally(c.deYear(r.year)) { p.deDone(r.year, it) } + Route.Roadmap -> { + var done = 0 + var total = 0 + c.german.filter { it.bool("unlocked") }.forEach { u -> + val n = u.strs("names").size + total += n + done += (1..n).count { p.germanDone(u.int("id"), it) } + } + done to total + } + Route.LitYears -> listOf( + tally(c.lit, p::litDone), tally(c.lit2, p::lit2Done), tally(c.lit3, p::lit3Done), tally(c.lit4, p::lit4Done), + ).let { parts -> parts.sumOf { it.first } to parts.sumOf { it.second } } + Route.LitMap -> tally(c.lit, p::litDone) + Route.Lit2Map -> tally(c.lit2, p::lit2Done) + Route.Lit3Map -> tally(c.lit3, p::lit3Done) + Route.Lit4Map -> tally(c.lit4, p::lit4Done) + Route.Mluvnice -> tally(c.mluvnice, p::mluvDone) + Route.ChemMap -> tally(c.chem, p::chemDone) + Route.BioMap -> tally(c.bio, p::bioDone) + Route.Mat0Map -> tally(c.mat0, p::mat0Done) + Route.MatMap -> tally(c.mat, p::matDone) + Route.Mat2Map -> tally(c.mat2, p::mat2Done) + Route.Mat3Map -> tally(c.mat3, p::mat3Done) + Route.Mat4Map -> tally(c.mat4, p::mat4Done) + Route.FyzMap -> tally(c.fyz, p::fyzDone) + Route.Fyz2Map -> tally(c.fyz2, p::fyz2Done) + Route.Fyz3Map -> tally(c.fyz3, p::fyz3Done) + Route.Fyz4Map -> tally(c.fyz4, p::fyz4Done) + Route.TgMap -> tally(c.tg, p::tgDone) + Route.Tg2Map -> tally(c.tg2, p::tg2Done) + Route.Tg3Map -> tally(c.tg3, p::tg3Done) + Route.Tg4Map -> tally(c.tg4, p::tg4Done) + Route.CsMap -> tally(c.cs, p::csDone) + Route.Cs2Map -> tally(c.cs2, p::cs2Done) + Route.Cs3Map -> tally(c.cs3, p::cs3Done) + Route.Cs4Map -> tally(c.cs4, p::cs4Done) + else -> null + } +} + private fun subjectSum(vm: AppViewModel, s: J): ProgressSum { val sum = ProgressSum() when (s.str("target")) { @@ -801,10 +879,10 @@ fun NetYearsScreen(vm: AppViewModel) { DetailScaffold(vm, vm.tr("net_years_title"), vm.tr("net_years_sub")) { inner -> LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { item { - NavRow(title = vm.tr("net_year1"), subtitle = vm.tr("net_sub"), leading = { Text("1.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.NetMap) } - NavRow(title = vm.tr("net_year2"), subtitle = vm.tr("net2_sub"), leading = { Text("2.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Net2Map) } - NavRow(title = vm.tr("net_year3"), subtitle = vm.tr("net3_sub"), leading = { Text("3.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Net3Map) } - NavRow(title = vm.tr("net_year4"), subtitle = vm.tr("net4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Net4Map) } + NavRow(title = vm.tr("net_year1"), subtitle = vm.tr("net_sub"), leading = { LeadingBadge("1") }, progress = routeProgress(vm, Route.NetMap)) { vm.go(Route.NetMap) } + NavRow(title = vm.tr("net_year2"), subtitle = vm.tr("net2_sub"), leading = { LeadingBadge("2") }, progress = routeProgress(vm, Route.Net2Map)) { vm.go(Route.Net2Map) } + NavRow(title = vm.tr("net_year3"), subtitle = vm.tr("net3_sub"), leading = { LeadingBadge("3") }, progress = routeProgress(vm, Route.Net3Map)) { vm.go(Route.Net3Map) } + NavRow(title = vm.tr("net_year4"), subtitle = vm.tr("net4_sub"), leading = { LeadingBadge("4") }, progress = routeProgress(vm, Route.Net4Map)) { vm.go(Route.Net4Map) } } } } @@ -815,10 +893,10 @@ fun HwYearsScreen(vm: AppViewModel) { DetailScaffold(vm, vm.tr("Technické vybavení"), vm.tr("hw_years_sub")) { inner -> LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { item { - NavRow(title = vm.tr("hw_year1"), subtitle = vm.tr("hw_sub"), leading = { Text("1.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.HwMap) } - NavRow(title = vm.tr("hw_year2"), subtitle = vm.tr("hw2_sub"), leading = { Text("2.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Hw2Map) } - NavRow(title = vm.tr("hw_year3"), subtitle = vm.tr("hw3_sub"), leading = { Text("3.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Hw3Map) } - NavRow(title = vm.tr("hw_year4"), subtitle = vm.tr("hw4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Hw4Map) } + NavRow(title = vm.tr("hw_year1"), subtitle = vm.tr("hw_sub"), leading = { LeadingBadge("1") }, progress = routeProgress(vm, Route.HwMap)) { vm.go(Route.HwMap) } + NavRow(title = vm.tr("hw_year2"), subtitle = vm.tr("hw2_sub"), leading = { LeadingBadge("2") }, progress = routeProgress(vm, Route.Hw2Map)) { vm.go(Route.Hw2Map) } + NavRow(title = vm.tr("hw_year3"), subtitle = vm.tr("hw3_sub"), leading = { LeadingBadge("3") }, progress = routeProgress(vm, Route.Hw3Map)) { vm.go(Route.Hw3Map) } + NavRow(title = vm.tr("hw_year4"), subtitle = vm.tr("hw4_sub"), leading = { LeadingBadge("4") }, progress = routeProgress(vm, Route.Hw4Map)) { vm.go(Route.Hw4Map) } } } } @@ -829,7 +907,7 @@ fun DeHomeScreen(vm: AppViewModel) { DetailScaffold(vm, vm.tr("Deutsch"), vm.tr("de_home_sub")) { inner -> LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { item { - NavRow(title = vm.tr("de_book_title"), subtitle = vm.tr("de_book_sub"), leading = { Text("U", style = MaterialTheme.typography.titleMedium) }) { + NavRow(title = vm.tr("de_book_title"), subtitle = vm.tr("de_book_sub"), leading = { LeadingBadge("U") }, progress = routeProgress(vm, Route.Roadmap)) { vm.go(Route.Roadmap) } } @@ -838,7 +916,8 @@ fun DeHomeScreen(vm: AppViewModel) { NavRow( title = vm.tr("de_year$year"), subtitle = vm.tr("de_y${year}_sub"), - leading = { Text("$year.", style = MaterialTheme.typography.titleMedium) }, + leading = { LeadingBadge("$year") }, + progress = routeProgress(vm, Route.DeMap(year)), ) { vm.go(Route.DeMap(year)) } } } @@ -854,7 +933,8 @@ fun EnYearsScreen(vm: AppViewModel) { NavRow( title = vm.tr("en_year$year"), subtitle = vm.tr("en_y${year}_sub"), - leading = { Text("$year.", style = MaterialTheme.typography.titleMedium) }, + leading = { LeadingBadge("$year") }, + progress = routeProgress(vm, Route.EnMap(year)), ) { vm.go(Route.EnMap(year)) } } } @@ -866,10 +946,10 @@ fun OnYearsScreen(vm: AppViewModel) { DetailScaffold(vm, vm.tr("Občanská nauka"), vm.tr("on_years_sub")) { inner -> LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { item { - NavRow(title = vm.tr("on_year1"), subtitle = vm.tr("on_sub"), leading = { Text("1.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.OnMap) } - NavRow(title = vm.tr("on_year2"), subtitle = vm.tr("on2_sub"), leading = { Text("2.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.On2Map) } - NavRow(title = vm.tr("on_year3"), subtitle = vm.tr("on3_sub"), leading = { Text("3.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.On3Map) } - NavRow(title = vm.tr("on_year4"), subtitle = vm.tr("on4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.On4Map) } + NavRow(title = vm.tr("on_year1"), subtitle = vm.tr("on_sub"), leading = { LeadingBadge("1") }, progress = routeProgress(vm, Route.OnMap)) { vm.go(Route.OnMap) } + NavRow(title = vm.tr("on_year2"), subtitle = vm.tr("on2_sub"), leading = { LeadingBadge("2") }, progress = routeProgress(vm, Route.On2Map)) { vm.go(Route.On2Map) } + NavRow(title = vm.tr("on_year3"), subtitle = vm.tr("on3_sub"), leading = { LeadingBadge("3") }, progress = routeProgress(vm, Route.On3Map)) { vm.go(Route.On3Map) } + NavRow(title = vm.tr("on_year4"), subtitle = vm.tr("on4_sub"), leading = { LeadingBadge("4") }, progress = routeProgress(vm, Route.On4Map)) { vm.go(Route.On4Map) } } } } @@ -880,10 +960,10 @@ fun LitYearsScreen(vm: AppViewModel) { DetailScaffold(vm, vm.tr("Literatura"), vm.tr("lit_years_sub")) { inner -> LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { item { - NavRow(title = vm.tr("lit_year1"), subtitle = vm.tr("lit_sub"), leading = { Text("1.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.LitMap) } - NavRow(title = vm.tr("lit_year2"), subtitle = vm.tr("lit2_sub"), leading = { Text("2.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Lit2Map) } - NavRow(title = vm.tr("lit_year3"), subtitle = vm.tr("lit3_sub"), leading = { Text("3.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Lit3Map) } - NavRow(title = vm.tr("lit_year4"), subtitle = vm.tr("lit4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Lit4Map) } + NavRow(title = vm.tr("lit_year1"), subtitle = vm.tr("lit_sub"), leading = { LeadingBadge("1") }, progress = routeProgress(vm, Route.LitMap)) { vm.go(Route.LitMap) } + NavRow(title = vm.tr("lit_year2"), subtitle = vm.tr("lit2_sub"), leading = { LeadingBadge("2") }, progress = routeProgress(vm, Route.Lit2Map)) { vm.go(Route.Lit2Map) } + NavRow(title = vm.tr("lit_year3"), subtitle = vm.tr("lit3_sub"), leading = { LeadingBadge("3") }, progress = routeProgress(vm, Route.Lit3Map)) { vm.go(Route.Lit3Map) } + NavRow(title = vm.tr("lit_year4"), subtitle = vm.tr("lit4_sub"), leading = { LeadingBadge("4") }, progress = routeProgress(vm, Route.Lit4Map)) { vm.go(Route.Lit4Map) } } } } @@ -894,11 +974,11 @@ fun MatYearsScreen(vm: AppViewModel) { DetailScaffold(vm, vm.tr("Matematika"), vm.tr("mat_years_sub")) { inner -> LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { item { - NavRow(title = vm.tr("mat0_year"), subtitle = vm.tr("mat0_sub"), leading = { Text("ZŠ", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Mat0Map) } - NavRow(title = vm.tr("mat_year1"), subtitle = vm.tr("mat_sub"), leading = { Text("1.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.MatMap) } - NavRow(title = vm.tr("mat_year2"), subtitle = vm.tr("mat2_sub"), leading = { Text("2.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Mat2Map) } - NavRow(title = vm.tr("mat_year3"), subtitle = vm.tr("mat3_sub"), leading = { Text("3.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Mat3Map) } - NavRow(title = vm.tr("mat_year4"), subtitle = vm.tr("mat4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Mat4Map) } + NavRow(title = vm.tr("mat0_year"), subtitle = vm.tr("mat0_sub"), leading = { LeadingBadge("ZŠ") }, progress = routeProgress(vm, Route.Mat0Map)) { vm.go(Route.Mat0Map) } + NavRow(title = vm.tr("mat_year1"), subtitle = vm.tr("mat_sub"), leading = { LeadingBadge("1") }, progress = routeProgress(vm, Route.MatMap)) { vm.go(Route.MatMap) } + NavRow(title = vm.tr("mat_year2"), subtitle = vm.tr("mat2_sub"), leading = { LeadingBadge("2") }, progress = routeProgress(vm, Route.Mat2Map)) { vm.go(Route.Mat2Map) } + NavRow(title = vm.tr("mat_year3"), subtitle = vm.tr("mat3_sub"), leading = { LeadingBadge("3") }, progress = routeProgress(vm, Route.Mat3Map)) { vm.go(Route.Mat3Map) } + NavRow(title = vm.tr("mat_year4"), subtitle = vm.tr("mat4_sub"), leading = { LeadingBadge("4") }, progress = routeProgress(vm, Route.Mat4Map)) { vm.go(Route.Mat4Map) } } } } @@ -909,10 +989,10 @@ fun CsYearsScreen(vm: AppViewModel) { DetailScaffold(vm, vm.tr("Programování"), vm.tr("cs_years_sub")) { inner -> LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { item { - NavRow(title = vm.tr("cs_year1"), subtitle = vm.tr("cs_sub"), leading = { Text("1.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.CsMap) } - NavRow(title = vm.tr("cs_year2"), subtitle = vm.tr("cs2_sub"), leading = { Text("2.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Cs2Map) } - NavRow(title = vm.tr("cs_year3"), subtitle = vm.tr("cs3_sub"), leading = { Text("3.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Cs3Map) } - NavRow(title = vm.tr("cs_year4"), subtitle = vm.tr("cs4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Cs4Map) } + NavRow(title = vm.tr("cs_year1"), subtitle = vm.tr("cs_sub"), leading = { LeadingBadge("1") }, progress = routeProgress(vm, Route.CsMap)) { vm.go(Route.CsMap) } + NavRow(title = vm.tr("cs_year2"), subtitle = vm.tr("cs2_sub"), leading = { LeadingBadge("2") }, progress = routeProgress(vm, Route.Cs2Map)) { vm.go(Route.Cs2Map) } + NavRow(title = vm.tr("cs_year3"), subtitle = vm.tr("cs3_sub"), leading = { LeadingBadge("3") }, progress = routeProgress(vm, Route.Cs3Map)) { vm.go(Route.Cs3Map) } + NavRow(title = vm.tr("cs_year4"), subtitle = vm.tr("cs4_sub"), leading = { LeadingBadge("4") }, progress = routeProgress(vm, Route.Cs4Map)) { vm.go(Route.Cs4Map) } } } } @@ -923,10 +1003,10 @@ fun TgYearsScreen(vm: AppViewModel) { DetailScaffold(vm, vm.tr("Technická grafika"), vm.tr("tg_years_sub")) { inner -> LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { item { - NavRow(title = vm.tr("tg_year1"), subtitle = vm.tr("tg_sub"), leading = { Text("1.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.TgMap) } - NavRow(title = vm.tr("tg_year2"), subtitle = vm.tr("tg2_sub"), leading = { Text("2.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Tg2Map) } - NavRow(title = vm.tr("tg_year3"), subtitle = vm.tr("tg3_sub"), leading = { Text("3.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Tg3Map) } - NavRow(title = vm.tr("tg_year4"), subtitle = vm.tr("tg4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Tg4Map) } + NavRow(title = vm.tr("tg_year1"), subtitle = vm.tr("tg_sub"), leading = { LeadingBadge("1") }, progress = routeProgress(vm, Route.TgMap)) { vm.go(Route.TgMap) } + NavRow(title = vm.tr("tg_year2"), subtitle = vm.tr("tg2_sub"), leading = { LeadingBadge("2") }, progress = routeProgress(vm, Route.Tg2Map)) { vm.go(Route.Tg2Map) } + NavRow(title = vm.tr("tg_year3"), subtitle = vm.tr("tg3_sub"), leading = { LeadingBadge("3") }, progress = routeProgress(vm, Route.Tg3Map)) { vm.go(Route.Tg3Map) } + NavRow(title = vm.tr("tg_year4"), subtitle = vm.tr("tg4_sub"), leading = { LeadingBadge("4") }, progress = routeProgress(vm, Route.Tg4Map)) { vm.go(Route.Tg4Map) } } } } @@ -937,10 +1017,10 @@ fun FyzYearsScreen(vm: AppViewModel) { DetailScaffold(vm, vm.tr("Fyzika"), vm.tr("fyz_years_sub")) { inner -> LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { item { - NavRow(title = vm.tr("fyz_year1"), subtitle = vm.tr("fyz_sub"), leading = { Text("1.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.FyzMap) } - NavRow(title = vm.tr("fyz_year2"), subtitle = vm.tr("fyz2_sub"), leading = { Text("2.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Fyz2Map) } - NavRow(title = vm.tr("fyz_year3"), subtitle = vm.tr("fyz3_sub"), leading = { Text("3.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Fyz3Map) } - NavRow(title = vm.tr("fyz_year4"), subtitle = vm.tr("fyz4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Fyz4Map) } + NavRow(title = vm.tr("fyz_year1"), subtitle = vm.tr("fyz_sub"), leading = { LeadingBadge("1") }, progress = routeProgress(vm, Route.FyzMap)) { vm.go(Route.FyzMap) } + NavRow(title = vm.tr("fyz_year2"), subtitle = vm.tr("fyz2_sub"), leading = { LeadingBadge("2") }, progress = routeProgress(vm, Route.Fyz2Map)) { vm.go(Route.Fyz2Map) } + NavRow(title = vm.tr("fyz_year3"), subtitle = vm.tr("fyz3_sub"), leading = { LeadingBadge("3") }, progress = routeProgress(vm, Route.Fyz3Map)) { vm.go(Route.Fyz3Map) } + NavRow(title = vm.tr("fyz_year4"), subtitle = vm.tr("fyz4_sub"), leading = { LeadingBadge("4") }, progress = routeProgress(vm, Route.Fyz4Map)) { vm.go(Route.Fyz4Map) } } } } @@ -954,11 +1034,17 @@ fun SciMapScreen(vm: AppViewModel) { NavRow( title = vm.tr("Chemie"), subtitle = vm.tr("chem_sub"), - leading = { Icon(Icons.Filled.Science, null, tint = MaterialTheme.colorScheme.primary) }, + leading = { LeadingIcon(Icons.Filled.Science) }, + progress = routeProgress(vm, Route.ChemMap), ) { vm.go(Route.ChemMap) } } item { - NavRow(title = vm.tr("Biologie"), subtitle = vm.tr("bio_sub")) { vm.go(Route.BioMap) } + NavRow( + title = vm.tr("Biologie"), + subtitle = vm.tr("bio_sub"), + leading = { LeadingIcon(Icons.Filled.Eco) }, + progress = routeProgress(vm, Route.BioMap), + ) { vm.go(Route.BioMap) } } } } @@ -969,20 +1055,26 @@ fun CzechMapScreen(vm: AppViewModel) { DetailScaffold(vm, vm.tr("Český jazyk a literatura"), vm.tr("czech_sub")) { inner -> LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { item { - NavRow(title = vm.tr("Literatura"), subtitle = vm.tr("lit_years_sub")) { vm.go(Route.LitYears) } + NavRow( + title = vm.tr("Literatura"), + subtitle = vm.tr("lit_years_sub"), + leading = { LeadingIcon(Icons.Filled.AutoStories) }, + progress = routeProgress(vm, Route.LitYears), + ) { vm.go(Route.LitYears) } } item { NavRow( title = vm.tr("Mluvnice"), subtitle = vm.tr("mluv_sub"), - leading = { Icon(Icons.Filled.FormatListNumbered, null, tint = MaterialTheme.colorScheme.primary) }, + leading = { LeadingIcon(Icons.Filled.FormatListNumbered) }, + progress = routeProgress(vm, Route.Mluvnice), ) { vm.go(Route.Mluvnice) } } item { NavRow( title = vm.tr("Maturitní četba"), subtitle = vm.tr("reading_sub"), - leading = { Icon(Icons.AutoMirrored.Filled.MenuBook, null, tint = MaterialTheme.colorScheme.primary) }, + leading = { LeadingIcon(Icons.AutoMirrored.Filled.MenuBook) }, ) { vm.go(Route.ReadingList) } } } @@ -1045,6 +1137,52 @@ fun BookScreen(vm: AppViewModel, id: String) { // --------------------------------------------------------------------------- // Lesson slides +/** One explanation slide: label, title, tip and numbered points. */ +@Composable +fun SlideContent(slide: J) { + Column { + Text(slide.str("kicker"), style = MaterialTheme.typography.labelMedium, color = MaterialTheme.colorScheme.onSurfaceVariant) + Text(slide.str("title"), style = MaterialTheme.typography.headlineSmall, modifier = Modifier.padding(top = 4.dp, bottom = 12.dp)) + slide.strOrNull("tip")?.let { tip -> + Card( + modifier = Modifier.fillMaxWidth().padding(bottom = 12.dp), + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.secondaryContainer), + ) { + Row(Modifier.padding(14.dp), verticalAlignment = Alignment.CenterVertically) { + Icon(Icons.Outlined.Lightbulb, null, tint = MaterialTheme.colorScheme.onSecondaryContainer) + Spacer(Modifier.width(10.dp)) + Text(tip, style = MaterialTheme.typography.bodyMedium) + } + } + } + slide.strs("lines").forEachIndexed { index, line -> + val numbered = Regex("""^(\d+)\.\s+(.*)$""").find(line) + val badge = numbered?.groupValues?.get(1) ?: "${index + 1}" + val text = numbered?.groupValues?.get(2) ?: line + Row( + Modifier + .fillMaxWidth() + .padding(vertical = 5.dp) + .background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.45f), androidx.compose.foundation.shape.RoundedCornerShape(16.dp)) + .padding(horizontal = 14.dp, vertical = 12.dp), + verticalAlignment = Alignment.Top, + ) { + Box( + Modifier + .padding(top = 2.dp) + .size(28.dp) + .background(MaterialTheme.colorScheme.primary.copy(alpha = 0.14f), CircleShape), + contentAlignment = Alignment.Center, + ) { + Text(badge, style = MaterialTheme.typography.labelLarge, color = MaterialTheme.colorScheme.primary) + } + Spacer(Modifier.width(12.dp)) + Text(text, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.weight(1f)) + } + } + } +} + @OptIn(ExperimentalFoundationApi::class) @Composable fun SlidesScreen( @@ -1095,52 +1233,13 @@ fun SlidesScreen( }, ) { inner -> HorizontalPager(state = pager, modifier = Modifier.fillMaxSize().padding(inner)) { page -> - val slide = slides[page] Column( Modifier .fillMaxSize() .verticalScroll(rememberScrollState()) .padding(horizontal = 16.dp, vertical = 12.dp), ) { - Text(slide.str("kicker"), style = MaterialTheme.typography.labelMedium, color = MaterialTheme.colorScheme.onSurfaceVariant) - Text(slide.str("title"), style = MaterialTheme.typography.headlineSmall, modifier = Modifier.padding(top = 4.dp, bottom = 12.dp)) - slide.strOrNull("tip")?.let { tip -> - Card( - modifier = Modifier.fillMaxWidth().padding(bottom = 12.dp), - colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.secondaryContainer), - ) { - Row(Modifier.padding(14.dp), verticalAlignment = Alignment.CenterVertically) { - Icon(Icons.Outlined.Lightbulb, null, tint = MaterialTheme.colorScheme.onSecondaryContainer) - Spacer(Modifier.width(10.dp)) - Text(tip, style = MaterialTheme.typography.bodyMedium) - } - } - } - slide.strs("lines").forEachIndexed { index, line -> - val numbered = Regex("""^(\d+)\.\s+(.*)$""").find(line) - val badge = numbered?.groupValues?.get(1) ?: "${index + 1}" - val text = numbered?.groupValues?.get(2) ?: line - Row( - Modifier - .fillMaxWidth() - .padding(vertical = 5.dp) - .background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.45f), androidx.compose.foundation.shape.RoundedCornerShape(16.dp)) - .padding(horizontal = 14.dp, vertical = 12.dp), - verticalAlignment = Alignment.Top, - ) { - Box( - Modifier - .padding(top = 2.dp) - .size(28.dp) - .background(MaterialTheme.colorScheme.primary.copy(alpha = 0.14f), CircleShape), - contentAlignment = Alignment.Center, - ) { - Text(badge, style = MaterialTheme.typography.labelLarge, color = MaterialTheme.colorScheme.primary) - } - Spacer(Modifier.width(12.dp)) - Text(text, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.weight(1f)) - } - } + SlideContent(slides[page]) Spacer(Modifier.height(24.dp)) } } diff --git a/content/aplikace/texty.md b/content/aplikace/texty.md index 3668457..34e1048 100644 --- a/content/aplikace/texty.md +++ b/content/aplikace/texty.md @@ -117,6 +117,7 @@ | search_page | Stránka | Page | | search_home | Úvod | Welcome | | subjects_title | Předměty | Subjects | +| subjects_soon | Připravujeme | Coming soon | | subjects_sub | Otevřený předmět spustí učební cestu. Zamčené zatím jen čekají. | An open subject opens its path. Locked ones are placeholders for now. | # Čeština @@ -262,6 +263,8 @@ | cs_run | Spustit | Run | | cs_reset | Obnovit ukázku | Reset example | | cs_running | Překládám… | Compiling… | +| cs_done | Hotovo | Done | +| cs_failed | Chyba | Error | | cs_empty | Zatím nic. Upravte kód a spusťte ho. | Nothing yet. Edit the code and run it. | | cs_timeout | Program běžel příliš dlouho a byl zastaven. | The program ran too long and was stopped. | | cs_unavailable | Tady C# nespustíte. Na počítači s nainstalovaným .NET se výstup objeví vedle kódu. | C# cannot run here. On a computer with .NET installed the output appears beside the code. | diff --git a/ios/Graduately/UI/Components.swift b/ios/Graduately/UI/Components.swift index a4bd6fe..ec5bd56 100644 --- a/ios/Graduately/UI/Components.swift +++ b/ios/Graduately/UI/Components.swift @@ -50,109 +50,47 @@ enum MacChrome { static let pageInset: CGFloat = 28 static let cardPadding: CGFloat = 22 static let listRowMinHeight: CGFloat = 52 - static let homeTileMin: CGFloat = 240 - static let homeTileHeight: CGFloat = 168 + static let homeTileMin: CGFloat = 260 + static let homeTileHeight: CGFloat = 172 static let pathNode: CGFloat = 84 + /// Pages stop growing here so cards and text keep their shape on wide displays. + static let contentWidth: CGFloat = 1240 + static let readableWidth: CGFloat = 860 + static let actionWidth: CGFloat = 520 } -enum MacGrid { - /// Picks a column count that fills the width and keeps tiles wider than they are tall. - static func columns(width: CGFloat, height: CGFloat, count: Int, minWidth: CGFloat, minHeight: CGFloat, spacing: CGFloat) -> Int { - guard count > 0, width > 0 else { return 1 } - let maxCols = max(1, min(count, Int((width + spacing) / (minWidth + spacing)))) - var best = maxCols - var bestScore = CGFloat.greatestFiniteMagnitude - let offeredH = height > 1 ? height : minHeight * CGFloat(count) - for cols in 1...maxCols { - let rows = Int(ceil(Double(count) / Double(cols))) - let tileW = (width - spacing * CGFloat(cols - 1)) / CGFloat(cols) - if tileW + 0.5 < minWidth { continue } - let needed = CGFloat(rows) * minHeight + spacing * CGFloat(max(rows - 1, 0)) - let tileH = max(minHeight, (offeredH - spacing * CGFloat(max(rows - 1, 0))) / CGFloat(rows)) - let aspect = tileH / max(tileW, 1) - let rem = count % cols - let ragged = rem == 0 ? 0 : CGFloat(1.5) - let lonely = rem == 1 ? CGFloat(1.4) : 0 - let tooTall = max(0, aspect - 1.05) * 3 - let overflow = max(0, needed - offeredH) - let score = abs(aspect - 0.78) * 2 + ragged + lonely + tooTall + overflow / 180 - if score < bestScore { - bestScore = score - best = cols - } - } - return best - } - - static func contentHeight(width: CGFloat, height: CGFloat, count: Int, minWidth: CGFloat, minHeight: CGFloat, spacing: CGFloat) -> CGFloat { - let cols = columns(width: width, height: height, count: count, minWidth: minWidth, minHeight: minHeight, spacing: spacing) - let rows = max(1, Int(ceil(Double(max(count, 1)) / Double(cols)))) - return CGFloat(rows) * minHeight + spacing * CGFloat(max(rows - 1, 0)) - } -} - -struct MacHeaderHeight: PreferenceKey { - static var defaultValue: CGFloat = 0 - static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { - value = max(value, nextValue()) - } -} - -/// Card grid that grows into the space it is given and scrolls only when the -/// minimum tile size no longer fits. +/// Fixed-height cards in as many columns as fit, centred and capped in width. struct MacFillGrid: View { var count: Int var minWidth: CGFloat = 240 var minHeight: CGFloat = 148 var spacing: CGFloat = 16 var inset: CGFloat = 28 - /// Home embeds the grid in its own scroll view. A second scroller here - /// swallows the wheel once a banner (the changelog) makes the page taller. - var scrolls = true + var maxTileWidth: CGFloat = 340 @ViewBuilder var content: (Int) -> Content var body: some View { GeometryReader { geo in - let innerW = max(geo.size.width - inset * 2, minWidth) - let available = max(geo.size.height - inset * 2, minHeight) - let cols = MacGrid.columns(width: innerW, height: available, count: count, minWidth: minWidth, minHeight: minHeight, spacing: spacing) - let rows = max(1, Int(ceil(Double(max(count, 1)) / Double(cols)))) - let floorH = CGFloat(rows) * minHeight + spacing * CGFloat(max(rows - 1, 0)) - let gridH = max(available, floorH) - let tileH = max(minHeight, (gridH - spacing * CGFloat(max(rows - 1, 0))) / CGFloat(rows)) - let tileW = (innerW - spacing * CGFloat(max(cols - 1, 0))) / CGFloat(max(cols, 1)) - let lastCount = count - (rows - 1) * cols - let tiles = VStack(spacing: spacing) { - ForEach(0.. 1 - HStack(spacing: spacing) { - ForEach(0.. 0 { + HStack(spacing: 10) { + ProgressBar(value: Double(progress.done) / Double(progress.total)) + Text("\(progress.done) / \(progress.total)") + .font(.subheadline.monospacedDigit()) .foregroundStyle(.secondary) } } - Text(title) - .font(.title2.weight(.semibold)) - .foregroundStyle(locked ? .secondary : .primary) - .fixedSize(horizontal: false, vertical: true) - if let subtitle, !subtitle.isEmpty { - Text(subtitle) - .font(.body) - .foregroundStyle(.secondary) - .fixedSize(horizontal: false, vertical: true) - } - Spacer(minLength: 0) } - .padding(22) + .padding(20) .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) - .softSurface(cornerRadius: 22) + .softSurface(cornerRadius: 20) + .contentShape(RoundedRectangle(cornerRadius: 20, style: .continuous)) } } #endif +/// Flat capsule bar. Unlike ProgressView it shows nothing at zero. +struct ProgressBar: View { + let value: Double + var height: CGFloat = 8 + + var body: some View { + GeometryReader { geo in + ZStack(alignment: .leading) { + Capsule().fill(Color.primary.opacity(0.08)) + if value > 0 { + Capsule() + .fill(Color.accentColor) + .frame(width: max(height, geo.size.width * min(value, 1))) + } + } + } + .frame(height: height) + .animation(Motion.settle, value: value) + } +} + +struct ProgressRing: View { + let value: Double + var lineWidth: CGFloat = 12 + @ViewBuilder var label: () -> Label + + var body: some View { + ZStack { + Circle().stroke(Color.primary.opacity(0.08), lineWidth: lineWidth) + if value > 0 { + Circle() + .trim(from: 0, to: min(value, 1)) + .stroke(Color.accentColor, style: StrokeStyle(lineWidth: lineWidth, lineCap: .round)) + .rotationEffect(.degrees(-90)) + } + label() + } + .animation(Motion.settle, value: value) + } +} + struct FlowLayout: Layout { var spacing: CGFloat = 6 @@ -574,15 +570,28 @@ extension View { #endif } - /// Full-width, leading-aligned content for exercise screens. + /// Leading-aligned content for exercise screens; on the Mac a centred readable column. func exerciseContent() -> some View { - frame(maxWidth: .infinity, alignment: .leading) - #if os(macOS) + #if os(macOS) + frame(maxWidth: MacChrome.readableWidth, alignment: .leading) .padding(.horizontal, MacChrome.pageInset) - .padding(.vertical, 20) - #else + .padding(.vertical, 28) + .frame(maxWidth: .infinity) + #else + frame(maxWidth: .infinity, alignment: .leading) .padding() - #endif + #endif + } + + /// Caps a Mac page at [width] and centres it; no-op on iOS. + @ViewBuilder + func macPage(_ width: CGFloat? = nil) -> some View { + #if os(macOS) + frame(maxWidth: width ?? MacChrome.contentWidth) + .frame(maxWidth: .infinity) + #else + self + #endif } /// macOS content lists use the inset style so they sit under the floating @@ -636,7 +645,8 @@ struct BottomAction: View { }()) #if os(macOS) .padding(.top, 12) - .padding(.bottom, 10) + .padding(.bottom, 14) + .frame(maxWidth: MacChrome.actionWidth) .frame(maxWidth: .infinity) #else .padding(.top, 8) @@ -645,10 +655,11 @@ struct BottomAction: View { } } -/// Primary + secondary text sized for desktop lists. +/// Primary + secondary text sized for desktop lists, with optional exercise progress. struct ListRowLabel: View { let title: String var subtitle: String? = nil + var progress: (done: Int, total: Int)? = nil var body: some View { VStack(alignment: .leading, spacing: 3) { @@ -665,6 +676,25 @@ struct ListRowLabel: View { #endif .foregroundStyle(.secondary) } + if let progress, progress.total > 0 { + ProgressLine(done: progress.done, total: progress.total) + .padding(.top, 4) + } + } + } +} + +/// Slim bar with "done / total" beside it, for list rows. +struct ProgressLine: View { + let done: Int + let total: Int + + var body: some View { + HStack(spacing: 8) { + ProgressBar(value: total == 0 ? 0 : Double(done) / Double(total), height: 5) + Text("\(done) / \(total)") + .font(.caption.monospacedDigit()) + .foregroundStyle(.secondary) } } } diff --git a/ios/Graduately/UI/Exercises.swift b/ios/Graduately/UI/Exercises.swift index e71f134..fb4ccea 100644 --- a/ios/Graduately/UI/Exercises.swift +++ b/ios/Graduately/UI/Exercises.swift @@ -12,21 +12,9 @@ struct ExerciseScaffold: View { var body: some View { let p = vm.palette - Group { - #if os(macOS) - GeometryReader { geo in - ScrollView { - VStack(alignment: .leading, spacing: 8) { content() } - .exerciseContent() - .frame(maxWidth: .infinity, minHeight: geo.size.height, alignment: .center) - } - } - #else - ScrollView { - VStack(alignment: .leading, spacing: 8) { content() } - .exerciseContent() - } - #endif + ScrollView { + VStack(alignment: .leading, spacing: 8) { content() } + .exerciseContent() } .scrollDismissesKeyboard(.interactively) .detailScreen(title, subtitle: sub) diff --git a/ios/Graduately/UI/PathMap.swift b/ios/Graduately/UI/PathMap.swift index 3271b2e..5c7e4e4 100644 --- a/ios/Graduately/UI/PathMap.swift +++ b/ios/Graduately/UI/PathMap.swift @@ -8,6 +8,8 @@ struct MapNode: Identifiable { let current: Bool var finish: Bool = false var destination: Route? + /// Glyph for a node that has no number, such as a side branch. + var symbol: String? = nil } private struct NodePos { @@ -256,6 +258,8 @@ struct PathMap: View { .overlay { if node.locked { Circle().strokeBorder(palette.lockedBorder, lineWidth: 1) + } else if !node.current && !node.done { + Circle().strokeBorder(palette.accent.opacity(0.55), lineWidth: 2.5) } } if node.finish { @@ -298,6 +302,13 @@ struct PathMap: View { #else .font(.headline.weight(.black)) #endif + } else if let symbol = node.symbol { + Image(systemName: symbol) + #if os(macOS) + .font(.title3.weight(.semibold)) + #else + .font(.body.weight(.semibold)) + #endif } Image(systemName: "checkmark") #if os(macOS) @@ -315,6 +326,14 @@ struct PathMap: View { .font(.headline.weight(.black)) #endif .foregroundStyle(node.current ? palette.onAccent : palette.text) + } else if let symbol = node.symbol { + Image(systemName: symbol) + #if os(macOS) + .font(.title2.weight(.semibold)) + #else + .font(.headline.weight(.semibold)) + #endif + .foregroundStyle(node.current ? palette.onAccent : palette.text) } } .frame(width: nodeSize, height: nodeSize) diff --git a/ios/Graduately/UI/Screens.swift b/ios/Graduately/UI/Screens.swift index e2f1ebb..02230cf 100644 --- a/ios/Graduately/UI/Screens.swift +++ b/ios/Graduately/UI/Screens.swift @@ -1,10 +1,10 @@ import SwiftUI +#if os(macOS) +import AppKit +#endif struct PracticeHome: View { @ObservedObject var vm: AppModel - #if os(macOS) - @State private var homeHeader: CGFloat = 280 - #endif var body: some View { #if os(macOS) @@ -18,111 +18,124 @@ struct PracticeHome: View { private var macHome: some View { let sum = summarize(vm.content, vm.progress) let pct = sum.totalEx == 0 ? 0 : Double(sum.doneEx) / Double(sum.totalEx) - let subjects = vm.content.subjects - return GeometryReader { geo in - let width = max(geo.size.width - MacChrome.pageInset * 2, MacChrome.homeTileMin) - let offered = max(geo.size.height - homeHeader - 20 - MacChrome.pageInset * 2, MacChrome.homeTileHeight) - let minGrid = MacGrid.contentHeight( - width: width, - height: offered, - count: subjects.count, - minWidth: MacChrome.homeTileMin, - minHeight: MacChrome.homeTileHeight, - spacing: 16 - ) - let gridH = max(minGrid, geo.size.height - homeHeader - 20 - MacChrome.pageInset * 2) - ScrollView { - VStack(alignment: .leading, spacing: 20) { - VStack(alignment: .leading, spacing: 16) { - if vm.showChangelog { - ChangelogCard(vm: vm) + let open = vm.content.subjects.filter { $0.bool("open") } + let soon = vm.content.subjects.filter { !$0.bool("open") } + return ScrollView { + VStack(alignment: .leading, spacing: 28) { + if vm.showChangelog { + ChangelogCard(vm: vm) + } + welcomeCard(sum: sum, pct: pct) + VStack(alignment: .leading, spacing: 14) { + Text(vm.tr("subjects_title")) + .font(.title2.weight(.semibold)) + LazyVGrid( + columns: [GridItem(.adaptive(minimum: MacChrome.homeTileMin), spacing: 16, alignment: .top)], + alignment: .leading, + spacing: 16 + ) { + ForEach(Array(open.enumerated()), id: \.offset) { _, s in + subjectTile(s) } - GlassCard { - VStack(alignment: .leading, spacing: 12) { - Text(vm.tr("welcome_title")) - .font(.largeTitle.bold()) - Text(vm.tr(Self.welcomeBodyKey)) - .font(.title3) - .foregroundStyle(.secondary) - .fixedSize(horizontal: false, vertical: true) - ProgressView(value: pct) - .animation(Motion.settle, value: pct) - Text(vm.fmt("stats_ex_fmt", sum.doneEx, sum.totalEx)) - .font(.subheadline) + } + } + if !soon.isEmpty { + VStack(alignment: .leading, spacing: 12) { + Text(vm.tr("subjects_soon")) + .font(.headline) + .foregroundStyle(.secondary) + FlowLayout(spacing: 10) { + ForEach(Array(soon.enumerated()), id: \.offset) { _, s in + Label(vm.tr(s.str("key")), systemImage: "lock.fill") + .font(.body) .foregroundStyle(.secondary) + .padding(.horizontal, 14) + .padding(.vertical, 9) + .background(Color.primary.opacity(0.05), in: Capsule()) } } - Text(vm.tr("subjects_title")) - .font(.title2.weight(.semibold)) } - .fixedSize(horizontal: false, vertical: true) - .background { - GeometryReader { proxy in - Color.clear.preference(key: MacHeaderHeight.self, value: proxy.size.height) - } - } - MacFillGrid( - count: subjects.count, - minWidth: MacChrome.homeTileMin, - minHeight: MacChrome.homeTileHeight, - spacing: 16, - inset: 0, - scrolls: false - ) { index in - subjectTile(subjects[index]) - } - .frame(height: gridH) } - .padding(MacChrome.pageInset) - .frame(maxWidth: .infinity, minHeight: geo.size.height, alignment: .topLeading) } - .onPreferenceChange(MacHeaderHeight.self) { homeHeader = $0 } + .padding(MacChrome.pageInset) + .macPage() } .navigationTitle("Graduately") } - @ViewBuilder - private func subjectTile(_ s: J) -> some View { - let open = s.bool("open") - let dest = s.strOrNull("target").flatMap(routeFromPage) - let part = subjectSum(vm, s) - let progress = open && part.totalEx > 0 ? Double(part.doneEx) / Double(part.totalEx) : 0 - Group { - if open, let dest, dest != .subjects, dest != .welcome { - NavigationLink(value: dest) { - subjectTileBody(s, open: true, progress: progress, part: part) + private func welcomeCard(sum: ProgressSum, pct: Double) -> some View { + GlassCard { + HStack(alignment: .center, spacing: 32) { + VStack(alignment: .leading, spacing: 12) { + Text(vm.tr("welcome_title")) + .font(.system(size: 34, weight: .bold)) + .fixedSize(horizontal: false, vertical: true) + Text(vm.tr(Self.welcomeBodyKey)) + .font(.title3) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) } - .buttonStyle(PressScaleStyle()) - } else { - subjectTileBody(s, open: false, progress: 0, part: part) + .frame(maxWidth: .infinity, alignment: .leading) + ProgressRing(value: pct) { + VStack(spacing: 2) { + Text(vm.fmt("stats_pct_fmt", Int((pct * 100).rounded(.down)))) + .font(.system(size: 26, weight: .bold, design: .rounded)) + .monospacedDigit() + Text(vm.fmt("stats_ex_fmt", sum.doneEx, sum.totalEx)) + .font(.subheadline.monospacedDigit()) + .foregroundStyle(.secondary) + } + } + .frame(width: 132, height: 132) } + .padding(.vertical, 6) } } - private func subjectTileBody(_ s: J, open: Bool, progress: Double, part: ProgressSum) -> some View { - VStack(alignment: .leading, spacing: 16) { - SubjectIcon(icon: s.str("icon"), open: open, size: 44) + @ViewBuilder + private func subjectTile(_ s: J) -> some View { + let dest = s.strOrNull("target").flatMap(routeFromPage) + if let dest, dest != .subjects, dest != .welcome { + NavigationLink(value: dest) { + subjectTileBody(s) + } + .buttonStyle(PressScaleStyle()) + } else { + subjectTileBody(s) + } + } + + private func subjectTileBody(_ s: J) -> some View { + let part = subjectSum(vm, s) + let progress = part.totalEx > 0 ? Double(part.doneEx) / Double(part.totalEx) : 0 + return VStack(alignment: .leading, spacing: 14) { + HStack(alignment: .center) { + SubjectIcon(icon: s.str("icon"), open: true, size: 40) + Spacer(minLength: 0) + Image(systemName: "chevron.right") + .font(.body.weight(.semibold)) + .foregroundStyle(.tertiary) + } Text(vm.tr(s.str("key"))) .font(.title3.weight(.semibold)) - .foregroundStyle(open ? .primary : .secondary) + .foregroundStyle(.primary) .multilineTextAlignment(.leading) + .lineLimit(2) Spacer(minLength: 0) - if open && part.totalEx > 0 { - ProgressView(value: progress) - .animation(Motion.settle, value: progress) + ProgressBar(value: progress) + HStack { Text(vm.fmt("stats_ex_fmt", part.doneEx, part.totalEx)) - .font(.subheadline) - .foregroundStyle(.secondary) - .monospacedDigit() - } else if !open { - Text(vm.tr("stats_locked")) - .font(.subheadline) - .foregroundStyle(.secondary) + Spacer(minLength: 0) + Text(vm.fmt("stats_pct_fmt", Int((progress * 100).rounded(.down)))) } + .font(.subheadline.monospacedDigit()) + .foregroundStyle(.secondary) } - .padding(22) - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + .padding(20) + .frame(maxWidth: .infinity, alignment: .topLeading) + .frame(height: MacChrome.homeTileHeight) .softSurface(cornerRadius: 22) + .contentShape(RoundedRectangle(cornerRadius: 22, style: .continuous)) } #endif @@ -160,16 +173,28 @@ struct PracticeHome: View { } Section(vm.tr("subjects_title")) { - ForEach(Array(vm.content.subjects.enumerated()), id: \.offset) { _, s in - let open = s.bool("open") + ForEach(Array(vm.content.subjects.filter { $0.bool("open") }.enumerated()), id: \.offset) { _, s in let dest = s.strOrNull("target").flatMap(routeFromPage) - if open, let dest, dest != .subjects, dest != .welcome { + if let dest, dest != .subjects, dest != .welcome { NavigationLink(value: dest) { subjectLabel(s) } } else { subjectLabel(s) - .foregroundStyle(.secondary) + } + } + } + + let soon = vm.content.subjects.filter { !$0.bool("open") } + if !soon.isEmpty { + Section(vm.tr("subjects_soon")) { + ForEach(Array(soon.enumerated()), id: \.offset) { _, s in + Label { + Text(vm.tr(s.str("key"))) + } icon: { + SubjectIcon(icon: s.str("icon"), open: false) + } + .foregroundStyle(.secondary) } } } @@ -179,15 +204,17 @@ struct PracticeHome: View { } private func subjectLabel(_ s: J) -> some View { - Label { - VStack(alignment: .leading, spacing: 2) { + let part = subjectSum(vm, s) + return Label { + VStack(alignment: .leading, spacing: 6) { Text(vm.tr(s.str("key"))) - if !s.bool("open") { - Text(vm.tr("stats_locked")).font(.caption).foregroundStyle(.secondary) + if part.totalEx > 0 { + ProgressLine(done: part.doneEx, total: part.totalEx) } } + .padding(.vertical, 2) } icon: { - SubjectIcon(icon: s.str("icon"), open: s.bool("open")) + SubjectIcon(icon: s.str("icon"), open: true) } } #endif @@ -423,7 +450,8 @@ struct SettingsScreen: View { } } .padding(MacChrome.pageInset) - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + .frame(maxWidth: MacChrome.contentWidth, maxHeight: .infinity, alignment: .topLeading) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) .navigationTitle(vm.tr("settings_title")) } @@ -566,7 +594,8 @@ struct StatsScreen: View { } } .padding(MacChrome.pageInset) - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + .frame(maxWidth: MacChrome.contentWidth, maxHeight: .infinity, alignment: .topLeading) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) .navigationTitle(vm.tr("stats_title")) } @@ -664,6 +693,68 @@ struct StatsScreen: View { } } +/// Done and total exercises behind one card of a years screen, or nil when there is no count. +func routeProgress(_ vm: AppModel, _ r: Route) -> (done: Int, total: Int)? { + let c = vm.content + let p = vm.progress + func tally(_ lessons: [J], _ done: (Int) -> Bool) -> (done: Int, total: Int) { + (lessons.filter { done($0.int("id")) }.count, lessons.count) + } + switch r { + case .netMap: return tally(c.netLessons, p.netDone) + case .net2Map: return tally(c.itLessons("net2")) { p.courseDone("net2", $0) } + case .net3Map: return tally(c.itLessons("net3")) { p.courseDone("net3", $0) } + case .net4Map: return tally(c.itLessons("net4")) { p.courseDone("net4", $0) } + case .hwMap: return tally(c.hw, p.hwDone) + case .hw2Map: return tally(c.itLessons("hw2")) { p.courseDone("hw2", $0) } + case .hw3Map: return tally(c.itLessons("hw3")) { p.courseDone("hw3", $0) } + case .hw4Map: return tally(c.itLessons("hw4")) { p.courseDone("hw4", $0) } + case .onMap: return tally(c.on, p.onDone) + case .on2Map: return tally(c.on2, p.on2Done) + case .on3Map: return tally(c.on3, p.on3Done) + case .on4Map: return tally(c.on4, p.on4Done) + case .enMap(let year): return tally(c.enYear(year)) { p.enDone(year, $0) } + case .deMap(let year): return tally(c.deYear(year)) { p.deDone(year, $0) } + case .roadmap: + var done = 0 + var total = 0 + for u in c.german where u.bool("unlocked") { + let n = u.strs("names").count + total += n + done += (0.. ProgressSum { var sum = ProgressSum() switch s.str("target") { @@ -795,6 +886,7 @@ struct SearchScreen: View { } } .padding(MacChrome.pageInset) + .macPage() } } .navigationTitle(vm.tr("search")) @@ -1108,7 +1200,7 @@ struct RoadmapScreen: View { vm.progress.germanDone(u.int("id"), $0 + 1) } return MapNode( - id: u.str("page"), + id: "unit-\(i)", label: u.str("title"), locked: !unlocked, done: done, @@ -1186,9 +1278,10 @@ struct UnitMapScreen: View { id: "vocab-\(unitId)", label: vm.tr("Vokabeltraining"), locked: false, - done: false, + done: vm.progress.vocabDone(unitId), current: false, - destination: .vocab(unitId) + destination: .vocab(unitId), + symbol: "character.book.closed.fill" ) ) : nil @@ -1256,9 +1349,9 @@ struct NetYearsScreen: View { ("net_year3", "net3_sub", .net3Map), ("net_year4", "net4_sub", .net4Map), ] - MacFillGrid(count: years.count, minWidth: 260, minHeight: 180) { i in + MacFillGrid(count: years.count, minWidth: 260, minHeight: 200) { i in NavigationLink(value: years[i].2) { - MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).") + MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).", progress: routeProgress(vm, years[i].2)) } .buttonStyle(PressScaleStyle()) } @@ -1267,16 +1360,16 @@ struct NetYearsScreen: View { #else List { NavigationLink(value: Route.netMap) { - ListRowLabel(title: vm.tr("net_year1"), subtitle: vm.tr("net_sub")) + ListRowLabel(title: vm.tr("net_year1"), subtitle: vm.tr("net_sub"), progress: routeProgress(vm, Route.netMap)) } NavigationLink(value: Route.net2Map) { - ListRowLabel(title: vm.tr("net_year2"), subtitle: vm.tr("net2_sub")) + ListRowLabel(title: vm.tr("net_year2"), subtitle: vm.tr("net2_sub"), progress: routeProgress(vm, Route.net2Map)) } NavigationLink(value: Route.net3Map) { - ListRowLabel(title: vm.tr("net_year3"), subtitle: vm.tr("net3_sub")) + ListRowLabel(title: vm.tr("net_year3"), subtitle: vm.tr("net3_sub"), progress: routeProgress(vm, Route.net3Map)) } NavigationLink(value: Route.net4Map) { - ListRowLabel(title: vm.tr("net_year4"), subtitle: vm.tr("net4_sub")) + ListRowLabel(title: vm.tr("net_year4"), subtitle: vm.tr("net4_sub"), progress: routeProgress(vm, Route.net4Map)) } } .navigationTitle(vm.tr("net_years_title")) @@ -1297,9 +1390,9 @@ struct HwYearsScreen: View { ("hw_year3", "hw3_sub", .hw3Map), ("hw_year4", "hw4_sub", .hw4Map), ] - MacFillGrid(count: years.count, minWidth: 260, minHeight: 180) { i in + MacFillGrid(count: years.count, minWidth: 260, minHeight: 200) { i in NavigationLink(value: years[i].2) { - MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).") + MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).", progress: routeProgress(vm, years[i].2)) } .buttonStyle(PressScaleStyle()) } @@ -1308,16 +1401,16 @@ struct HwYearsScreen: View { #else List { NavigationLink(value: Route.hwMap) { - ListRowLabel(title: vm.tr("hw_year1"), subtitle: vm.tr("hw_sub")) + ListRowLabel(title: vm.tr("hw_year1"), subtitle: vm.tr("hw_sub"), progress: routeProgress(vm, Route.hwMap)) } NavigationLink(value: Route.hw2Map) { - ListRowLabel(title: vm.tr("hw_year2"), subtitle: vm.tr("hw2_sub")) + ListRowLabel(title: vm.tr("hw_year2"), subtitle: vm.tr("hw2_sub"), progress: routeProgress(vm, Route.hw2Map)) } NavigationLink(value: Route.hw3Map) { - ListRowLabel(title: vm.tr("hw_year3"), subtitle: vm.tr("hw3_sub")) + ListRowLabel(title: vm.tr("hw_year3"), subtitle: vm.tr("hw3_sub"), progress: routeProgress(vm, Route.hw3Map)) } NavigationLink(value: Route.hw4Map) { - ListRowLabel(title: vm.tr("hw_year4"), subtitle: vm.tr("hw4_sub")) + ListRowLabel(title: vm.tr("hw_year4"), subtitle: vm.tr("hw4_sub"), progress: routeProgress(vm, Route.hw4Map)) } } .navigationTitle(vm.tr("Technické vybavení")) @@ -1339,9 +1432,9 @@ struct DeHomeScreen: View { ("3", "de_year3", "de_y3_sub", .deMap(3)), ("4", "de_year4", "de_y4_sub", .deMap(4)), ] - MacFillGrid(count: rows.count, minWidth: 260, minHeight: 180) { i in + MacFillGrid(count: rows.count, minWidth: 260, minHeight: 200) { i in NavigationLink(value: rows[i].3) { - MacLinkCard(title: vm.tr(rows[i].1), subtitle: vm.tr(rows[i].2), badge: rows[i].0) + MacLinkCard(title: vm.tr(rows[i].1), subtitle: vm.tr(rows[i].2), badge: rows[i].0, progress: routeProgress(vm, rows[i].3)) } .buttonStyle(PressScaleStyle()) } @@ -1350,11 +1443,11 @@ struct DeHomeScreen: View { #else List { NavigationLink(value: Route.roadmap) { - ListRowLabel(title: vm.tr("de_book_title"), subtitle: vm.tr("de_book_sub")) + ListRowLabel(title: vm.tr("de_book_title"), subtitle: vm.tr("de_book_sub"), progress: routeProgress(vm, Route.roadmap)) } ForEach(1...4, id: \.self) { year in NavigationLink(value: Route.deMap(year)) { - ListRowLabel(title: vm.tr("de_year\(year)"), subtitle: vm.tr("de_y\(year)_sub")) + ListRowLabel(title: vm.tr("de_year\(year)"), subtitle: vm.tr("de_y\(year)_sub"), progress: routeProgress(vm, Route.deMap(year))) } } } @@ -1375,9 +1468,9 @@ struct EnYearsScreen: View { ("en_year3", "en_y3_sub", .enMap(3)), ("en_year4", "en_y4_sub", .enMap(4)), ] - MacFillGrid(count: years.count, minWidth: 260, minHeight: 180) { i in + MacFillGrid(count: years.count, minWidth: 260, minHeight: 200) { i in NavigationLink(value: years[i].2) { - MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).") + MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).", progress: routeProgress(vm, years[i].2)) } .buttonStyle(PressScaleStyle()) } @@ -1387,7 +1480,7 @@ struct EnYearsScreen: View { List { ForEach(1...4, id: \.self) { year in NavigationLink(value: Route.enMap(year)) { - ListRowLabel(title: vm.tr("en_year\(year)"), subtitle: vm.tr("en_y\(year)_sub")) + ListRowLabel(title: vm.tr("en_year\(year)"), subtitle: vm.tr("en_y\(year)_sub"), progress: routeProgress(vm, Route.enMap(year))) } } } @@ -1409,9 +1502,9 @@ struct OnYearsScreen: View { ("on_year3", "on3_sub", .on3Map), ("on_year4", "on4_sub", .on4Map), ] - MacFillGrid(count: years.count, minWidth: 260, minHeight: 180) { i in + MacFillGrid(count: years.count, minWidth: 260, minHeight: 200) { i in NavigationLink(value: years[i].2) { - MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).") + MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).", progress: routeProgress(vm, years[i].2)) } .buttonStyle(PressScaleStyle()) } @@ -1420,16 +1513,16 @@ struct OnYearsScreen: View { #else List { NavigationLink(value: Route.onMap) { - ListRowLabel(title: vm.tr("on_year1"), subtitle: vm.tr("on_sub")) + ListRowLabel(title: vm.tr("on_year1"), subtitle: vm.tr("on_sub"), progress: routeProgress(vm, Route.onMap)) } NavigationLink(value: Route.on2Map) { - ListRowLabel(title: vm.tr("on_year2"), subtitle: vm.tr("on2_sub")) + ListRowLabel(title: vm.tr("on_year2"), subtitle: vm.tr("on2_sub"), progress: routeProgress(vm, Route.on2Map)) } NavigationLink(value: Route.on3Map) { - ListRowLabel(title: vm.tr("on_year3"), subtitle: vm.tr("on3_sub")) + ListRowLabel(title: vm.tr("on_year3"), subtitle: vm.tr("on3_sub"), progress: routeProgress(vm, Route.on3Map)) } NavigationLink(value: Route.on4Map) { - ListRowLabel(title: vm.tr("on_year4"), subtitle: vm.tr("on4_sub")) + ListRowLabel(title: vm.tr("on_year4"), subtitle: vm.tr("on4_sub"), progress: routeProgress(vm, Route.on4Map)) } } .navigationTitle(vm.tr("Občanská nauka")) @@ -1575,12 +1668,15 @@ struct SlidesScreen: View { .padding(.horizontal) .padding(.top, 8) .padding(.bottom, 4) + #if os(macOS) + .macPage(MacChrome.actionWidth) + #endif } } private func slidePage(_ slide: J) -> some View { ScrollView { - slideStack(slide) + SlideBody(slide: slide) .padding(.horizontal, slideInset) .padding(.top, 22) .padding(.bottom, 28) @@ -1596,6 +1692,15 @@ struct SlidesScreen: View { 16 #endif } +} + +/// One explanation slide: label, title, tip and numbered points. +struct SlideBody: View { + let slide: J + + var body: some View { + slideStack(slide) + } private func slideStack(_ slide: J) -> some View { VStack(alignment: .leading, spacing: 18) { @@ -1724,9 +1829,9 @@ struct LitYearsScreen: View { ("lit_year3", "lit3_sub", .lit3Map), ("lit_year4", "lit4_sub", .lit4Map), ] - MacFillGrid(count: years.count, minWidth: 260, minHeight: 180) { i in + MacFillGrid(count: years.count, minWidth: 260, minHeight: 200) { i in NavigationLink(value: years[i].2) { - MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).") + MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).", progress: routeProgress(vm, years[i].2)) } .buttonStyle(PressScaleStyle()) } @@ -1735,16 +1840,16 @@ struct LitYearsScreen: View { #else List { NavigationLink(value: Route.litMap) { - ListRowLabel(title: vm.tr("lit_year1"), subtitle: vm.tr("lit_sub")) + ListRowLabel(title: vm.tr("lit_year1"), subtitle: vm.tr("lit_sub"), progress: routeProgress(vm, Route.litMap)) } NavigationLink(value: Route.lit2Map) { - ListRowLabel(title: vm.tr("lit_year2"), subtitle: vm.tr("lit2_sub")) + ListRowLabel(title: vm.tr("lit_year2"), subtitle: vm.tr("lit2_sub"), progress: routeProgress(vm, Route.lit2Map)) } NavigationLink(value: Route.lit3Map) { - ListRowLabel(title: vm.tr("lit_year3"), subtitle: vm.tr("lit3_sub")) + ListRowLabel(title: vm.tr("lit_year3"), subtitle: vm.tr("lit3_sub"), progress: routeProgress(vm, Route.lit3Map)) } NavigationLink(value: Route.lit4Map) { - ListRowLabel(title: vm.tr("lit_year4"), subtitle: vm.tr("lit4_sub")) + ListRowLabel(title: vm.tr("lit_year4"), subtitle: vm.tr("lit4_sub"), progress: routeProgress(vm, Route.lit4Map)) } } .navigationTitle(vm.tr("Literatura")) @@ -1766,9 +1871,9 @@ struct MatYearsScreen: View { ("mat_year3", "mat3_sub", .mat3Map, "3."), ("mat_year4", "mat4_sub", .mat4Map, "4."), ] - MacFillGrid(count: years.count, minWidth: 260, minHeight: 180) { i in + MacFillGrid(count: years.count, minWidth: 260, minHeight: 200) { i in NavigationLink(value: years[i].2) { - MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: years[i].3) + MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: years[i].3, progress: routeProgress(vm, years[i].2)) } .buttonStyle(PressScaleStyle()) } @@ -1777,19 +1882,19 @@ struct MatYearsScreen: View { #else List { NavigationLink(value: Route.mat0Map) { - ListRowLabel(title: vm.tr("mat0_year"), subtitle: vm.tr("mat0_sub")) + ListRowLabel(title: vm.tr("mat0_year"), subtitle: vm.tr("mat0_sub"), progress: routeProgress(vm, Route.mat0Map)) } NavigationLink(value: Route.matMap) { - ListRowLabel(title: vm.tr("mat_year1"), subtitle: vm.tr("mat_sub")) + ListRowLabel(title: vm.tr("mat_year1"), subtitle: vm.tr("mat_sub"), progress: routeProgress(vm, Route.matMap)) } NavigationLink(value: Route.mat2Map) { - ListRowLabel(title: vm.tr("mat_year2"), subtitle: vm.tr("mat2_sub")) + ListRowLabel(title: vm.tr("mat_year2"), subtitle: vm.tr("mat2_sub"), progress: routeProgress(vm, Route.mat2Map)) } NavigationLink(value: Route.mat3Map) { - ListRowLabel(title: vm.tr("mat_year3"), subtitle: vm.tr("mat3_sub")) + ListRowLabel(title: vm.tr("mat_year3"), subtitle: vm.tr("mat3_sub"), progress: routeProgress(vm, Route.mat3Map)) } NavigationLink(value: Route.mat4Map) { - ListRowLabel(title: vm.tr("mat_year4"), subtitle: vm.tr("mat4_sub")) + ListRowLabel(title: vm.tr("mat_year4"), subtitle: vm.tr("mat4_sub"), progress: routeProgress(vm, Route.mat4Map)) } } .navigationTitle(vm.tr("Matematika")) @@ -1810,9 +1915,9 @@ struct CsYearsScreen: View { ("cs_year3", "cs3_sub", .cs3Map), ("cs_year4", "cs4_sub", .cs4Map), ] - MacFillGrid(count: years.count, minWidth: 260, minHeight: 180) { i in + MacFillGrid(count: years.count, minWidth: 260, minHeight: 200) { i in NavigationLink(value: years[i].2) { - MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).") + MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).", progress: routeProgress(vm, years[i].2)) } .buttonStyle(PressScaleStyle()) } @@ -1821,16 +1926,16 @@ struct CsYearsScreen: View { #else List { NavigationLink(value: Route.csMap) { - ListRowLabel(title: vm.tr("cs_year1"), subtitle: vm.tr("cs_sub")) + ListRowLabel(title: vm.tr("cs_year1"), subtitle: vm.tr("cs_sub"), progress: routeProgress(vm, Route.csMap)) } NavigationLink(value: Route.cs2Map) { - ListRowLabel(title: vm.tr("cs_year2"), subtitle: vm.tr("cs2_sub")) + ListRowLabel(title: vm.tr("cs_year2"), subtitle: vm.tr("cs2_sub"), progress: routeProgress(vm, Route.cs2Map)) } NavigationLink(value: Route.cs3Map) { - ListRowLabel(title: vm.tr("cs_year3"), subtitle: vm.tr("cs3_sub")) + ListRowLabel(title: vm.tr("cs_year3"), subtitle: vm.tr("cs3_sub"), progress: routeProgress(vm, Route.cs3Map)) } NavigationLink(value: Route.cs4Map) { - ListRowLabel(title: vm.tr("cs_year4"), subtitle: vm.tr("cs4_sub")) + ListRowLabel(title: vm.tr("cs_year4"), subtitle: vm.tr("cs4_sub"), progress: routeProgress(vm, Route.cs4Map)) } } .navigationTitle(vm.tr("Programování")) @@ -1851,9 +1956,9 @@ struct TgYearsScreen: View { ("tg_year3", "tg3_sub", .tg3Map), ("tg_year4", "tg4_sub", .tg4Map), ] - MacFillGrid(count: years.count, minWidth: 260, minHeight: 180) { i in + MacFillGrid(count: years.count, minWidth: 260, minHeight: 200) { i in NavigationLink(value: years[i].2) { - MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).") + MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).", progress: routeProgress(vm, years[i].2)) } .buttonStyle(PressScaleStyle()) } @@ -1862,16 +1967,16 @@ struct TgYearsScreen: View { #else List { NavigationLink(value: Route.tgMap) { - ListRowLabel(title: vm.tr("tg_year1"), subtitle: vm.tr("tg_sub")) + ListRowLabel(title: vm.tr("tg_year1"), subtitle: vm.tr("tg_sub"), progress: routeProgress(vm, Route.tgMap)) } NavigationLink(value: Route.tg2Map) { - ListRowLabel(title: vm.tr("tg_year2"), subtitle: vm.tr("tg2_sub")) + ListRowLabel(title: vm.tr("tg_year2"), subtitle: vm.tr("tg2_sub"), progress: routeProgress(vm, Route.tg2Map)) } NavigationLink(value: Route.tg3Map) { - ListRowLabel(title: vm.tr("tg_year3"), subtitle: vm.tr("tg3_sub")) + ListRowLabel(title: vm.tr("tg_year3"), subtitle: vm.tr("tg3_sub"), progress: routeProgress(vm, Route.tg3Map)) } NavigationLink(value: Route.tg4Map) { - ListRowLabel(title: vm.tr("tg_year4"), subtitle: vm.tr("tg4_sub")) + ListRowLabel(title: vm.tr("tg_year4"), subtitle: vm.tr("tg4_sub"), progress: routeProgress(vm, Route.tg4Map)) } } .navigationTitle(vm.tr("Technická grafika")) @@ -1892,9 +1997,9 @@ struct FyzYearsScreen: View { ("fyz_year3", "fyz3_sub", .fyz3Map), ("fyz_year4", "fyz4_sub", .fyz4Map), ] - MacFillGrid(count: years.count, minWidth: 260, minHeight: 180) { i in + MacFillGrid(count: years.count, minWidth: 260, minHeight: 200) { i in NavigationLink(value: years[i].2) { - MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).") + MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).", progress: routeProgress(vm, years[i].2)) } .buttonStyle(PressScaleStyle()) } @@ -1903,16 +2008,16 @@ struct FyzYearsScreen: View { #else List { NavigationLink(value: Route.fyzMap) { - ListRowLabel(title: vm.tr("fyz_year1"), subtitle: vm.tr("fyz_sub")) + ListRowLabel(title: vm.tr("fyz_year1"), subtitle: vm.tr("fyz_sub"), progress: routeProgress(vm, Route.fyzMap)) } NavigationLink(value: Route.fyz2Map) { - ListRowLabel(title: vm.tr("fyz_year2"), subtitle: vm.tr("fyz2_sub")) + ListRowLabel(title: vm.tr("fyz_year2"), subtitle: vm.tr("fyz2_sub"), progress: routeProgress(vm, Route.fyz2Map)) } NavigationLink(value: Route.fyz3Map) { - ListRowLabel(title: vm.tr("fyz_year3"), subtitle: vm.tr("fyz3_sub")) + ListRowLabel(title: vm.tr("fyz_year3"), subtitle: vm.tr("fyz3_sub"), progress: routeProgress(vm, Route.fyz3Map)) } NavigationLink(value: Route.fyz4Map) { - ListRowLabel(title: vm.tr("fyz_year4"), subtitle: vm.tr("fyz4_sub")) + ListRowLabel(title: vm.tr("fyz_year4"), subtitle: vm.tr("fyz4_sub"), progress: routeProgress(vm, Route.fyz4Map)) } } .navigationTitle(vm.tr("Fyzika")) @@ -1927,15 +2032,15 @@ struct SciMapScreen: View { var body: some View { #if os(macOS) - MacFillGrid(count: 2, minWidth: 260, minHeight: 180) { i in + MacFillGrid(count: 2, minWidth: 260, minHeight: 200) { i in if i == 0 { NavigationLink(value: Route.chemMap) { - MacLinkCard(title: vm.tr("Chemie"), subtitle: vm.tr("chem_sub"), systemImage: "flask.fill") + MacLinkCard(title: vm.tr("Chemie"), subtitle: vm.tr("chem_sub"), systemImage: "flask.fill", progress: routeProgress(vm, .chemMap)) } .buttonStyle(PressScaleStyle()) } else { NavigationLink(value: Route.bioMap) { - MacLinkCard(title: vm.tr("Biologie"), subtitle: vm.tr("bio_sub"), systemImage: "leaf.fill") + MacLinkCard(title: vm.tr("Biologie"), subtitle: vm.tr("bio_sub"), systemImage: "leaf.fill", progress: routeProgress(vm, .bioMap)) } .buttonStyle(PressScaleStyle()) } @@ -1946,14 +2051,14 @@ struct SciMapScreen: View { List { NavigationLink(value: Route.chemMap) { Label { - ListRowLabel(title: vm.tr("Chemie"), subtitle: vm.tr("chem_sub")) + ListRowLabel(title: vm.tr("Chemie"), subtitle: vm.tr("chem_sub"), progress: routeProgress(vm, .chemMap)) } icon: { Image(systemName: "flask.fill") } } NavigationLink(value: Route.bioMap) { Label { - ListRowLabel(title: vm.tr("Biologie"), subtitle: vm.tr("bio_sub")) + ListRowLabel(title: vm.tr("Biologie"), subtitle: vm.tr("bio_sub"), progress: routeProgress(vm, .bioMap)) } icon: { Image(systemName: "leaf.fill") } @@ -1971,21 +2076,21 @@ struct CzechMapScreen: View { var body: some View { #if os(macOS) - MacFillGrid(count: 3, minWidth: 260, minHeight: 180) { i in + MacFillGrid(count: 3, minWidth: 260, minHeight: 200) { i in switch i { case 1: NavigationLink(value: Route.mluvnice) { - MacLinkCard(title: vm.tr("Mluvnice"), systemImage: "textformat") + MacLinkCard(title: vm.tr("Mluvnice"), subtitle: vm.tr("mluv_sub"), systemImage: "textformat", progress: routeProgress(vm, .mluvnice)) } .buttonStyle(PressScaleStyle()) case 2: NavigationLink(value: Route.readingList) { - MacLinkCard(title: vm.tr("Maturitní četba"), systemImage: "books.vertical") + MacLinkCard(title: vm.tr("Maturitní četba"), subtitle: vm.tr("reading_sub"), systemImage: "books.vertical") } .buttonStyle(PressScaleStyle()) default: NavigationLink(value: Route.litYears) { - MacLinkCard(title: vm.tr("Literatura"), systemImage: "book.closed") + MacLinkCard(title: vm.tr("Literatura"), subtitle: vm.tr("lit_years_sub"), systemImage: "book.closed", progress: routeProgress(vm, .litYears)) } .buttonStyle(PressScaleStyle()) } @@ -1996,21 +2101,21 @@ struct CzechMapScreen: View { List { NavigationLink(value: Route.litYears) { Label { - Text(vm.tr("Literatura")) + ListRowLabel(title: vm.tr("Literatura"), subtitle: vm.tr("lit_years_sub"), progress: routeProgress(vm, .litYears)) } icon: { Image(systemName: "book.closed") } } NavigationLink(value: Route.mluvnice) { Label { - Text(vm.tr("Mluvnice")) + ListRowLabel(title: vm.tr("Mluvnice"), subtitle: vm.tr("mluv_sub"), progress: routeProgress(vm, .mluvnice)) } icon: { Image(systemName: "textformat") } } NavigationLink(value: Route.readingList) { Label { - Text(vm.tr("Maturitní četba")) + ListRowLabel(title: vm.tr("Maturitní četba"), subtitle: vm.tr("reading_sub")) } icon: { Image(systemName: "books.vertical") } @@ -3100,27 +3205,38 @@ struct CodeLessonScreen: View { @State private var code = "" @State private var stdin = "" @State private var output = "" - @State private var running = false + @State private var status = "" + + private var running: Bool { status == "running" } + private var needsInput: Bool { code.contains("ReadLine") } var body: some View { GeometryReader { geo in - if geo.size.width >= 840 { - HStack(alignment: .top, spacing: 16) { - explanation.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) - editor.frame(maxWidth: .infinity, maxHeight: .infinity) - outputPane.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + if geo.size.width >= 900 { + HStack(alignment: .top, spacing: 28) { + ScrollView { + explanation + .padding(.vertical, 4) + } + .frame(width: min(560, geo.size.width * 0.4)) + workspace(height: geo.size.height) } + .padding(.horizontal, 28) + .padding(.vertical, 20) + .frame(maxWidth: 1640) + .frame(maxWidth: .infinity, maxHeight: .infinity) } else { ScrollView { - VStack(alignment: .leading, spacing: 16) { + VStack(alignment: .leading, spacing: 20) { explanation - editor.frame(minHeight: 240) - outputPane.frame(minHeight: 160) + editorCard.frame(height: 320) + if needsInput { inputField } + outputCard.frame(height: 220) } + .padding(16) } } } - .padding(16) .detailScreen(vm.tr(lesson.str("titleKey")), subtitle: vm.tr(lesson.str("subKey"))) .onAppear { if code.isEmpty { code = lesson.str("code") } @@ -3128,84 +3244,154 @@ struct CodeLessonScreen: View { } private var explanation: some View { - ScrollView { - VStack(alignment: .leading, spacing: 16) { - ForEach(Array(lesson.arr("slides").enumerated()), id: \.offset) { _, slide in - VStack(alignment: .leading, spacing: 8) { - Text(slide.str("kicker")) - .font(.subheadline.weight(.semibold)) - .foregroundStyle(Color.accentColor) - Text(slide.str("title")) - .font(.title3.bold()) - ForEach(slide.strs("lines"), id: \.self) { line in - Text("• \(line)") - .frame(maxWidth: .infinity, alignment: .leading) - } - if let tip = slide.strOrNull("tip") { - Text(tip) - .foregroundStyle(.secondary) - } - } - } + VStack(alignment: .leading, spacing: 34) { + ForEach(Array(lesson.arr("slides").enumerated()), id: \.offset) { _, slide in + SlideBody(slide: slide) } } } - private var editor: some View { - VStack(alignment: .leading, spacing: 8) { - Text(vm.tr("cs_code")).font(.headline) - TextEditor(text: $code) - .font(.system(.body, design: .monospaced)) - .scrollContentBackground(.hidden) - .padding(8) + private func workspace(height: CGFloat) -> some View { + VStack(spacing: 16) { + editorCard + .frame(maxHeight: .infinity) + if needsInput { inputField } + outputCard + .frame(height: max(180, height * 0.32)) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + + private var editorCard: some View { + VStack(spacing: 0) { + HStack(spacing: 10) { + Image(systemName: "chevron.left.forwardslash.chevron.right") + .foregroundStyle(Color.accentColor) + Text("Program.cs") + .font(.headline.monospaced()) + Spacer(minLength: 8) + Button { + code = lesson.str("code") + output = "" + status = "" + } label: { + Label(vm.tr("cs_reset"), systemImage: "arrow.counterclockwise") + } + .buttonStyle(.bordered) + .disabled(running) + Button { run() } label: { + Label(vm.tr("cs_run"), systemImage: "play.fill") + .padding(.horizontal, 4) + } + .buttonStyle(.borderedProminent) + .keyboardShortcut(.return, modifiers: .command) + .help("⌘↩") + .disabled(running) + } + #if os(macOS) + .controlSize(.large) + #endif + .padding(.horizontal, 14) + .padding(.vertical, 10) + Divider().opacity(0.5) + editorField .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(codeFill, in: RoundedRectangle(cornerRadius: 12)) - Text(vm.tr("cs_stdin")) - .font(.caption) + } + .background(codeFill, in: RoundedRectangle(cornerRadius: 16, style: .continuous)) + .overlay { + RoundedRectangle(cornerRadius: 16, style: .continuous) + .strokeBorder(.separator.opacity(0.6), lineWidth: 0.5) + } + } + + @ViewBuilder + private var editorField: some View { + #if os(macOS) + CodeEditor(text: $code) + #else + TextEditor(text: $code) + .font(.system(size: 15, design: .monospaced)) + .textInputAutocapitalization(.never) + .autocorrectionDisabled() + .scrollContentBackground(.hidden) + .padding(8) + #endif + } + + private var inputField: some View { + VStack(alignment: .leading, spacing: 6) { + Label(vm.tr("cs_stdin"), systemImage: "keyboard") + .font(.subheadline) .foregroundStyle(.secondary) TextField("", text: $stdin, axis: .vertical) - .font(.system(.body, design: .monospaced)) - .lineLimit(2...4) + .font(.system(size: 15, design: .monospaced)) + .lineLimit(1...4) .textFieldStyle(.roundedBorder) } } - private var outputPane: some View { - VStack(alignment: .leading, spacing: 8) { - HStack { - Button(vm.tr("cs_run")) { run() } - .buttonStyle(.borderedProminent) - .disabled(running) - Button(vm.tr("cs_reset")) { code = lesson.str("code") } - .disabled(running) + private var outputCard: some View { + VStack(spacing: 0) { + HStack(spacing: 10) { + Image(systemName: "terminal") + .foregroundStyle(.secondary) + Text(vm.tr("cs_output")) + .font(.headline) + Spacer(minLength: 8) + statusBadge } - Text(vm.tr("cs_output")).font(.headline) + .padding(.horizontal, 14) + .frame(height: 44) + Divider().opacity(0.5) ScrollView { Text(output.isEmpty ? vm.tr("cs_empty") : output) - .font(.system(.body, design: .monospaced)) + .font(.system(size: 15, design: .monospaced)) + .foregroundStyle(output.isEmpty || running ? .secondary : .primary) .frame(maxWidth: .infinity, alignment: .topLeading) .textSelection(.enabled) + .padding(14) } - .padding(12) - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) - .background(codeFill, in: RoundedRectangle(cornerRadius: 12)) + } + .background(codeFill, in: RoundedRectangle(cornerRadius: 16, style: .continuous)) + .overlay { + RoundedRectangle(cornerRadius: 16, style: .continuous) + .strokeBorder(.separator.opacity(0.6), lineWidth: 0.5) } } - private var codeFill: Color { + @ViewBuilder + private var statusBadge: some View { + switch status { + case "running": + ProgressView().controlSize(.small) + case "ok": + Label(vm.tr("cs_done"), systemImage: "checkmark.circle.fill") + .font(.subheadline.weight(.semibold)) + .foregroundStyle(vm.palette.success) + case "fail", "timeout": + Label(vm.tr("cs_failed"), systemImage: "xmark.octagon.fill") + .font(.subheadline.weight(.semibold)) + .foregroundStyle(vm.palette.error) + default: + EmptyView() + } + } + + private var codeFill: AnyShapeStyle { #if os(macOS) - Color(.textBackgroundColor).opacity(0.55) + AnyShapeStyle(.background.secondary) #else - Color(.secondarySystemBackground) + AnyShapeStyle(Color(.secondarySystemBackground)) #endif } private func run() { - running = true + guard !running else { return } + status = "running" output = vm.tr("cs_running") - CSharpRunner.run(code, stdin: stdin) { status, text in - running = false - switch status { + CSharpRunner.run(code, stdin: needsInput ? stdin : "") { result, text in + status = result + switch result { case "unavailable": output = vm.tr("cs_unavailable") case "timeout": @@ -3213,11 +3399,125 @@ struct CodeLessonScreen: View { default: output = text.isEmpty ? vm.tr("cs_empty") : text } - if status == "ok" { onSuccess() } + if result == "ok" { onSuccess() } } } } +#if os(macOS) +/// Plain-text editor for C#: monospaced, coloured, and without smart quotes or +/// autocorrect, which would turn "text" into “text” and break the program. +struct CodeEditor: NSViewRepresentable { + @Binding var text: String + + func makeCoordinator() -> Coordinator { Coordinator(text: $text) } + + func makeNSView(context: Context) -> NSScrollView { + let scroll = NSTextView.scrollableTextView() + scroll.drawsBackground = false + scroll.autohidesScrollers = true + guard let view = scroll.documentView as? NSTextView else { return scroll } + view.delegate = context.coordinator + view.isRichText = false + view.allowsUndo = true + view.isAutomaticQuoteSubstitutionEnabled = false + view.isAutomaticDashSubstitutionEnabled = false + view.isAutomaticTextReplacementEnabled = false + view.isAutomaticSpellingCorrectionEnabled = false + view.isContinuousSpellCheckingEnabled = false + view.isGrammarCheckingEnabled = false + view.isAutomaticLinkDetectionEnabled = false + view.smartInsertDeleteEnabled = false + view.drawsBackground = false + view.font = CSharpStyle.font + view.textContainerInset = NSSize(width: 10, height: 12) + view.typingAttributes = CSharpStyle.base + view.string = text + CSharpStyle.highlight(view.textStorage) + return scroll + } + + func updateNSView(_ scroll: NSScrollView, context: Context) { + context.coordinator.text = $text + guard let view = scroll.documentView as? NSTextView, view.string != text else { return } + view.string = text + CSharpStyle.highlight(view.textStorage) + } + + final class Coordinator: NSObject, NSTextViewDelegate { + var text: Binding + + init(text: Binding) { + self.text = text + } + + func textDidChange(_ notification: Notification) { + guard let view = notification.object as? NSTextView else { return } + text.wrappedValue = view.string + CSharpStyle.highlight(view.textStorage) + view.typingAttributes = CSharpStyle.base + } + } +} + +enum CSharpStyle { + static let font = NSFont.monospacedSystemFont(ofSize: 15, weight: .regular) + static var base: [NSAttributedString.Key: Any] { [.font: font, .foregroundColor: NSColor.labelColor] } + + private static func adaptive(_ light: Int, _ dark: Int) -> NSColor { + func color(_ hex: Int) -> NSColor { + NSColor( + srgbRed: CGFloat((hex >> 16) & 0xFF) / 255, + green: CGFloat((hex >> 8) & 0xFF) / 255, + blue: CGFloat(hex & 0xFF) / 255, + alpha: 1 + ) + } + return NSColor(name: nil) { appearance in + appearance.bestMatch(from: [.aqua, .darkAqua]) == .darkAqua ? color(dark) : color(light) + } + } + + private static let keywords = [ + "abstract", "as", "async", "await", "base", "bool", "break", "case", "catch", "char", "class", + "const", "continue", "decimal", "default", "do", "double", "else", "enum", "false", "finally", + "float", "for", "foreach", "get", "if", "in", "int", "interface", "internal", "is", "long", + "namespace", "new", "null", "object", "out", "override", "private", "protected", "public", + "readonly", "record", "ref", "return", "sealed", "set", "static", "string", "struct", "switch", + "this", "throw", "true", "try", "using", "var", "virtual", "void", "while", + ] + + private static let rules: [(NSRegularExpression, NSColor)] = { + func re(_ pattern: String) -> NSRegularExpression { + try! NSRegularExpression(pattern: pattern) + } + return [ + (re("\\b(?:" + keywords.joined(separator: "|") + ")\\b"), adaptive(0x9B2393, 0xFF7AB2)), + (re("\\b(?:Console|Math|List|Dictionary|File|Task|Exception|Func|Action|String|DateTime|Random)\\b"), adaptive(0x0B4F79, 0x5DD8FF)), + (re("\\b\\d+(?:\\.\\d+)?\\b"), adaptive(0x1C00CF, 0xD9C97C)), + (re("\\$?@?\"(?:\\\\.|[^\"\\\\\\n])*\"|'(?:\\\\.|[^'\\\\\\n])'"), adaptive(0xC41A16, 0xFC6A5D)), + (re("//[^\\n]*|/\\*[\\s\\S]*?\\*/"), adaptive(0x5D6C79, 0x7F8C98)), + ] + }() + + static func highlight(_ storage: NSTextStorage?) { + guard let storage else { return } + let text = storage.string + let full = NSRange(location: 0, length: (text as NSString).length) + storage.beginEditing() + storage.setAttributes(base, range: full) + for (pattern, color) in rules { + pattern.enumerateMatches(in: text, range: full) { match, _, _ in + if let range = match?.range { + storage.addAttribute(.foregroundColor, value: color, range: range) + } + } + } + storage.endEditing() + } +} +#endif + enum CSharpRunner { static func run(_ source: String, stdin: String, done: @escaping (String, String) -> Void) { #if os(macOS) From ec7aa631597fc785cd6a45ffaa375f1e060dc175 Mon Sep 17 00:00:00 2001 From: pepaskopec-blip Date: Sun, 11 Oct 2026 18:12:17 +0200 Subject: [PATCH 5/7] Open presentation graphics as four years from Photoshop to the exam campaign. Co-authored-by: Cursor --- README.md | 5 +- .../shared/src/org/maturita/maturita/App.kt | 59 ++++++++ .../src/org/maturita/maturita/AppViewModel.kt | 20 +++ .../shared/src/org/maturita/maturita/Route.kt | 55 +++++++ .../src/org/maturita/maturita/data/Content.kt | 8 + .../maturita/maturita/data/ProgressStore.kt | 40 +++++ .../src/org/maturita/maturita/ui/Exercises.kt | 24 +++ .../src/org/maturita/maturita/ui/Screens.kt | 48 ++++++ content/README.md | 4 +- content/aplikace/novinky.txt | 6 + content/aplikace/predmety.md | 2 +- content/aplikace/texty.md | 14 ++ .../1-rocnik/01-bitmapa-a-vektor.en.md | 6 + .../1-rocnik/01-bitmapa-a-vektor.md | 79 ++++++++++ .../1-rocnik/02-rozliseni-a-barva.en.md | 6 + .../1-rocnik/02-rozliseni-a-barva.md | 79 ++++++++++ .../1-rocnik/03-prostredi-photoshopu.en.md | 6 + .../1-rocnik/03-prostredi-photoshopu.md | 79 ++++++++++ .../1-rocnik/04-vrstvy.en.md | 6 + .../prezentacni-grafika/1-rocnik/04-vrstvy.md | 79 ++++++++++ .../1-rocnik/05-vybery.en.md | 6 + .../prezentacni-grafika/1-rocnik/05-vybery.md | 79 ++++++++++ .../1-rocnik/06-retus.en.md | 6 + .../prezentacni-grafika/1-rocnik/06-retus.md | 79 ++++++++++ .../1-rocnik/07-text-a-barvy.en.md | 6 + .../1-rocnik/07-text-a-barvy.md | 79 ++++++++++ .../1-rocnik/08-zadani-fotografie.en.md | 6 + .../1-rocnik/08-zadani-fotografie.md | 79 ++++++++++ .../2-rocnik/01-masky.en.md | 6 + .../prezentacni-grafika/2-rocnik/01-masky.md | 79 ++++++++++ .../2-rocnik/02-urovne-a-krivky.en.md | 6 + .../2-rocnik/02-urovne-a-krivky.md | 79 ++++++++++ .../2-rocnik/03-chytre-objekty-a-filtry.en.md | 6 + .../2-rocnik/03-chytre-objekty-a-filtry.md | 79 ++++++++++ .../2-rocnik/04-tvary-a-styly.en.md | 6 + .../2-rocnik/04-tvary-a-styly.md | 79 ++++++++++ .../2-rocnik/05-kompozice-plakatu.en.md | 6 + .../2-rocnik/05-kompozice-plakatu.md | 79 ++++++++++ .../2-rocnik/06-mockup.en.md | 6 + .../prezentacni-grafika/2-rocnik/06-mockup.md | 79 ++++++++++ .../2-rocnik/07-export-web-a-tisk.en.md | 6 + .../2-rocnik/07-export-web-a-tisk.md | 79 ++++++++++ .../2-rocnik/08-zadani-plakat.en.md | 6 + .../2-rocnik/08-zadani-plakat.md | 79 ++++++++++ .../3-rocnik/01-projekt-a-media.en.md | 6 + .../3-rocnik/01-projekt-a-media.md | 79 ++++++++++ .../3-rocnik/02-casova-osa-a-strih.en.md | 6 + .../3-rocnik/02-casova-osa-a-strih.md | 79 ++++++++++ .../3-rocnik/03-tempo-a-prechody.en.md | 6 + .../3-rocnik/03-tempo-a-prechody.md | 79 ++++++++++ .../3-rocnik/04-titulky.en.md | 6 + .../3-rocnik/04-titulky.md | 79 ++++++++++ .../3-rocnik/05-zvuk.en.md | 6 + .../prezentacni-grafika/3-rocnik/05-zvuk.md | 79 ++++++++++ .../3-rocnik/06-barva-ve-videu.en.md | 6 + .../3-rocnik/06-barva-ve-videu.md | 79 ++++++++++ .../3-rocnik/07-export-videa.en.md | 6 + .../3-rocnik/07-export-videa.md | 79 ++++++++++ .../3-rocnik/08-zadani-video.en.md | 6 + .../3-rocnik/08-zadani-video.md | 79 ++++++++++ .../4-rocnik/01-illustrator.en.md | 6 + .../4-rocnik/01-illustrator.md | 79 ++++++++++ .../4-rocnik/02-logo.en.md | 6 + .../prezentacni-grafika/4-rocnik/02-logo.md | 79 ++++++++++ .../4-rocnik/03-after-effects.en.md | 6 + .../4-rocnik/03-after-effects.md | 79 ++++++++++ .../4-rocnik/04-klicove-snimky.en.md | 6 + .../4-rocnik/04-klicove-snimky.md | 79 ++++++++++ .../4-rocnik/05-indesign.en.md | 6 + .../4-rocnik/05-indesign.md | 79 ++++++++++ .../4-rocnik/06-propojeni.en.md | 6 + .../4-rocnik/06-propojeni.md | 79 ++++++++++ .../4-rocnik/07-export-a-formaty.en.md | 6 + .../4-rocnik/07-export-a-formaty.md | 79 ++++++++++ .../4-rocnik/08-maturitni-zadani.en.md | 6 + .../4-rocnik/08-maturitni-zadani.md | 79 ++++++++++ ios/Graduately/AppModel.swift | 20 +++ ios/Graduately/Data/Content.swift | 12 ++ ios/Graduately/Data/ProgressStore.swift | 40 +++++ ios/Graduately/Route.swift | 81 ++++++++++ ios/Graduately/UI/Components.swift | 1 + ios/Graduately/UI/ExercisesMore.swift | 33 +++++ ios/Graduately/UI/Screens.swift | 138 ++++++++++++++++++ scripts/build-content.py | 2 + 84 files changed, 3328 insertions(+), 4 deletions(-) create mode 100644 content/prezentacni-grafika/1-rocnik/01-bitmapa-a-vektor.en.md create mode 100644 content/prezentacni-grafika/1-rocnik/01-bitmapa-a-vektor.md create mode 100644 content/prezentacni-grafika/1-rocnik/02-rozliseni-a-barva.en.md create mode 100644 content/prezentacni-grafika/1-rocnik/02-rozliseni-a-barva.md create mode 100644 content/prezentacni-grafika/1-rocnik/03-prostredi-photoshopu.en.md create mode 100644 content/prezentacni-grafika/1-rocnik/03-prostredi-photoshopu.md create mode 100644 content/prezentacni-grafika/1-rocnik/04-vrstvy.en.md create mode 100644 content/prezentacni-grafika/1-rocnik/04-vrstvy.md create mode 100644 content/prezentacni-grafika/1-rocnik/05-vybery.en.md create mode 100644 content/prezentacni-grafika/1-rocnik/05-vybery.md create mode 100644 content/prezentacni-grafika/1-rocnik/06-retus.en.md create mode 100644 content/prezentacni-grafika/1-rocnik/06-retus.md create mode 100644 content/prezentacni-grafika/1-rocnik/07-text-a-barvy.en.md create mode 100644 content/prezentacni-grafika/1-rocnik/07-text-a-barvy.md create mode 100644 content/prezentacni-grafika/1-rocnik/08-zadani-fotografie.en.md create mode 100644 content/prezentacni-grafika/1-rocnik/08-zadani-fotografie.md create mode 100644 content/prezentacni-grafika/2-rocnik/01-masky.en.md create mode 100644 content/prezentacni-grafika/2-rocnik/01-masky.md create mode 100644 content/prezentacni-grafika/2-rocnik/02-urovne-a-krivky.en.md create mode 100644 content/prezentacni-grafika/2-rocnik/02-urovne-a-krivky.md create mode 100644 content/prezentacni-grafika/2-rocnik/03-chytre-objekty-a-filtry.en.md create mode 100644 content/prezentacni-grafika/2-rocnik/03-chytre-objekty-a-filtry.md create mode 100644 content/prezentacni-grafika/2-rocnik/04-tvary-a-styly.en.md create mode 100644 content/prezentacni-grafika/2-rocnik/04-tvary-a-styly.md create mode 100644 content/prezentacni-grafika/2-rocnik/05-kompozice-plakatu.en.md create mode 100644 content/prezentacni-grafika/2-rocnik/05-kompozice-plakatu.md create mode 100644 content/prezentacni-grafika/2-rocnik/06-mockup.en.md create mode 100644 content/prezentacni-grafika/2-rocnik/06-mockup.md create mode 100644 content/prezentacni-grafika/2-rocnik/07-export-web-a-tisk.en.md create mode 100644 content/prezentacni-grafika/2-rocnik/07-export-web-a-tisk.md create mode 100644 content/prezentacni-grafika/2-rocnik/08-zadani-plakat.en.md create mode 100644 content/prezentacni-grafika/2-rocnik/08-zadani-plakat.md create mode 100644 content/prezentacni-grafika/3-rocnik/01-projekt-a-media.en.md create mode 100644 content/prezentacni-grafika/3-rocnik/01-projekt-a-media.md create mode 100644 content/prezentacni-grafika/3-rocnik/02-casova-osa-a-strih.en.md create mode 100644 content/prezentacni-grafika/3-rocnik/02-casova-osa-a-strih.md create mode 100644 content/prezentacni-grafika/3-rocnik/03-tempo-a-prechody.en.md create mode 100644 content/prezentacni-grafika/3-rocnik/03-tempo-a-prechody.md create mode 100644 content/prezentacni-grafika/3-rocnik/04-titulky.en.md create mode 100644 content/prezentacni-grafika/3-rocnik/04-titulky.md create mode 100644 content/prezentacni-grafika/3-rocnik/05-zvuk.en.md create mode 100644 content/prezentacni-grafika/3-rocnik/05-zvuk.md create mode 100644 content/prezentacni-grafika/3-rocnik/06-barva-ve-videu.en.md create mode 100644 content/prezentacni-grafika/3-rocnik/06-barva-ve-videu.md create mode 100644 content/prezentacni-grafika/3-rocnik/07-export-videa.en.md create mode 100644 content/prezentacni-grafika/3-rocnik/07-export-videa.md create mode 100644 content/prezentacni-grafika/3-rocnik/08-zadani-video.en.md create mode 100644 content/prezentacni-grafika/3-rocnik/08-zadani-video.md create mode 100644 content/prezentacni-grafika/4-rocnik/01-illustrator.en.md create mode 100644 content/prezentacni-grafika/4-rocnik/01-illustrator.md create mode 100644 content/prezentacni-grafika/4-rocnik/02-logo.en.md create mode 100644 content/prezentacni-grafika/4-rocnik/02-logo.md create mode 100644 content/prezentacni-grafika/4-rocnik/03-after-effects.en.md create mode 100644 content/prezentacni-grafika/4-rocnik/03-after-effects.md create mode 100644 content/prezentacni-grafika/4-rocnik/04-klicove-snimky.en.md create mode 100644 content/prezentacni-grafika/4-rocnik/04-klicove-snimky.md create mode 100644 content/prezentacni-grafika/4-rocnik/05-indesign.en.md create mode 100644 content/prezentacni-grafika/4-rocnik/05-indesign.md create mode 100644 content/prezentacni-grafika/4-rocnik/06-propojeni.en.md create mode 100644 content/prezentacni-grafika/4-rocnik/06-propojeni.md create mode 100644 content/prezentacni-grafika/4-rocnik/07-export-a-formaty.en.md create mode 100644 content/prezentacni-grafika/4-rocnik/07-export-a-formaty.md create mode 100644 content/prezentacni-grafika/4-rocnik/08-maturitni-zadani.en.md create mode 100644 content/prezentacni-grafika/4-rocnik/08-maturitni-zadani.md diff --git a/README.md b/README.md index 22b57c4..9ab13ae 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,9 @@ Intel Mac, jiná architektura nebo úpravy kódu → [sestavení ze zdroje](#ses ročníky 2–4 po 8 lekcích (monitory, sestavení, RAID, servery, údržba) - **Technická grafika** — 4 ročníky AutoCADu, v každém 8 lekcí: výkres a souřadnice, úpravy a kóty, pohledy a tisk, 3D a maturitní zadání +- **Prezentační grafika** — 4 ročníky, v každém 8 lekcí: Photoshop od pixelu + po plakát, Premiere Pro a ve čtvrtém ročníku Illustrator, After Effects + a InDesign - **Programování** — 4 ročníky C# po vzoru W3Schools. U každé lekce se kód píše a spouští vedle výkladu, od výpisu po třídy, LINQ a async - **Český jazyk a literatura** — literatura (4 ročníky, 32 lekcí podle osnov @@ -94,7 +97,7 @@ Intel Mac, jiná architektura nebo úpravy kódu → [sestavení ze zdroje](#ses ### Použití -1. **Úvod** ukáže postup a mřížku předmětů. Otevřený předmět spustí cestu. Zamčené dlaždice zatím nic neotevřou: prezentační grafika a programové vybavení. +1. **Úvod** ukáže postup a mřížku předmětů. Otevřený předmět spustí cestu. Zamčená dlaždice zatím nic neotevře: programové vybavení. 2. U němčiny je učebnice (Neue Freunde, Aus aller Welt, Bei uns zu Hause) a čtyři ročníky. Ostatní otevřené předměty mají ročníky, lekce nebo knihy. 3. Z cesty se otevře výklad, poslech, čtení, psaní nebo kvíz. 4. **Statistiky**, **Nastavení** a **Hledat** jsou v postranním panelu na Macu. Na telefonu a v desktopové aplikaci jsou na záložkách, hledání navíc ikonou lupy. Zkratka je `Cmd/Ctrl+K`. diff --git a/android/shared/src/org/maturita/maturita/App.kt b/android/shared/src/org/maturita/maturita/App.kt index df44197..a814af3 100644 --- a/android/shared/src/org/maturita/maturita/App.kt +++ b/android/shared/src/org/maturita/maturita/App.kt @@ -32,6 +32,8 @@ import org.maturita.maturita.ui.HwQuizScreen import org.maturita.maturita.ui.HwYearsScreen import org.maturita.maturita.ui.LessonListScreen import org.maturita.maturita.ui.FyzQuizScreen +import org.maturita.maturita.ui.PgQuizScreen +import org.maturita.maturita.ui.PgYearsScreen import org.maturita.maturita.ui.TgQuizScreen import org.maturita.maturita.ui.TgYearsScreen import org.maturita.maturita.ui.MathPracticeScreen @@ -586,6 +588,63 @@ private fun MaturitaAppBody(vm: AppViewModel) { } } is Route.Tg4Ex -> TgQuizScreen(vm, 4, r.id) + Route.PgYears -> PgYearsScreen(vm) + Route.PgMap -> LessonListScreen( + vm, vm.tr("pg_year1"), vm.tr("pg_sub"), + vm.content.pg.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.pgDone(id), Route.PgLesson(id)) + }, + ) + is Route.PgLesson -> { + val l = vm.content.pgLesson(r.id) + if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) { + vm.replace(Route.PgEx(r.id)) + } + } + is Route.PgEx -> PgQuizScreen(vm, 1, r.id) + Route.Pg2Map -> LessonListScreen( + vm, vm.tr("pg_year2"), vm.tr("pg2_sub"), + vm.content.pg2.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.pg2Done(id), Route.Pg2Lesson(id)) + }, + ) + is Route.Pg2Lesson -> { + val l = vm.content.pg2Lesson(r.id) + if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) { + vm.replace(Route.Pg2Ex(r.id)) + } + } + is Route.Pg2Ex -> PgQuizScreen(vm, 2, r.id) + Route.Pg3Map -> LessonListScreen( + vm, vm.tr("pg_year3"), vm.tr("pg3_sub"), + vm.content.pg3.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.pg3Done(id), Route.Pg3Lesson(id)) + }, + ) + is Route.Pg3Lesson -> { + val l = vm.content.pg3Lesson(r.id) + if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) { + vm.replace(Route.Pg3Ex(r.id)) + } + } + is Route.Pg3Ex -> PgQuizScreen(vm, 3, r.id) + Route.Pg4Map -> LessonListScreen( + vm, vm.tr("pg_year4"), vm.tr("pg4_sub"), + vm.content.pg4.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.pg4Done(id), Route.Pg4Lesson(id)) + }, + ) + is Route.Pg4Lesson -> { + val l = vm.content.pg4Lesson(r.id) + if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) { + vm.replace(Route.Pg4Ex(r.id)) + } + } + is Route.Pg4Ex -> PgQuizScreen(vm, 4, r.id) Route.CsYears -> CsYearsScreen(vm) Route.CsMap -> LessonListScreen( vm, vm.tr("cs_year1"), vm.tr("cs_sub"), diff --git a/android/shared/src/org/maturita/maturita/AppViewModel.kt b/android/shared/src/org/maturita/maturita/AppViewModel.kt index f12a718..2eeca6a 100644 --- a/android/shared/src/org/maturita/maturita/AppViewModel.kt +++ b/android/shared/src/org/maturita/maturita/AppViewModel.kt @@ -262,6 +262,26 @@ class AppViewModel(val content: Content, val platform: Platform) { refresh() } + fun markPg(id: Int) { + progress.markPg(id) + refresh() + } + + fun markPg2(id: Int) { + progress.markPg2(id) + refresh() + } + + fun markPg3(id: Int) { + progress.markPg3(id) + refresh() + } + + fun markPg4(id: Int) { + progress.markPg4(id) + refresh() + } + fun markCs(id: Int) { progress.markCs(id) refresh() diff --git a/android/shared/src/org/maturita/maturita/Route.kt b/android/shared/src/org/maturita/maturita/Route.kt index 254a296..705f15d 100644 --- a/android/shared/src/org/maturita/maturita/Route.kt +++ b/android/shared/src/org/maturita/maturita/Route.kt @@ -118,6 +118,19 @@ sealed class Route(val page: String) { data object Tg4Map : Route("tg4map") data class Tg4Lesson(val id: Int) : Route("tg4unit$id") data class Tg4Ex(val id: Int) : Route("tg4ex$id") + data object PgYears : Route("pgyears") + data object PgMap : Route("pgmap") + data class PgLesson(val id: Int) : Route("pgunit$id") + data class PgEx(val id: Int) : Route("pgex$id") + data object Pg2Map : Route("pg2map") + data class Pg2Lesson(val id: Int) : Route("pg2unit$id") + data class Pg2Ex(val id: Int) : Route("pg2ex$id") + data object Pg3Map : Route("pg3map") + data class Pg3Lesson(val id: Int) : Route("pg3unit$id") + data class Pg3Ex(val id: Int) : Route("pg3ex$id") + data object Pg4Map : Route("pg4map") + data class Pg4Lesson(val id: Int) : Route("pg4unit$id") + data class Pg4Ex(val id: Int) : Route("pg4ex$id") data object CsYears : Route("csyears") data object CsMap : Route("csmap") data class CsLesson(val id: Int) : Route("csunit$id") @@ -272,6 +285,19 @@ sealed class Route(val page: String) { Tg4Map -> listOf(Subjects, TgYears, Tg4Map) is Tg4Lesson -> listOf(Subjects, TgYears, Tg4Map, this) is Tg4Ex -> listOf(Subjects, TgYears, Tg4Map, Tg4Lesson(id), this) + PgYears -> listOf(Subjects, PgYears) + PgMap -> listOf(Subjects, PgYears, PgMap) + is PgLesson -> listOf(Subjects, PgYears, PgMap, this) + is PgEx -> listOf(Subjects, PgYears, PgMap, PgLesson(id), this) + Pg2Map -> listOf(Subjects, PgYears, Pg2Map) + is Pg2Lesson -> listOf(Subjects, PgYears, Pg2Map, this) + is Pg2Ex -> listOf(Subjects, PgYears, Pg2Map, Pg2Lesson(id), this) + Pg3Map -> listOf(Subjects, PgYears, Pg3Map) + is Pg3Lesson -> listOf(Subjects, PgYears, Pg3Map, this) + is Pg3Ex -> listOf(Subjects, PgYears, Pg3Map, Pg3Lesson(id), this) + Pg4Map -> listOf(Subjects, PgYears, Pg4Map) + is Pg4Lesson -> listOf(Subjects, PgYears, Pg4Map, this) + is Pg4Ex -> listOf(Subjects, PgYears, Pg4Map, Pg4Lesson(id), this) CsYears -> listOf(Subjects, CsYears) CsMap -> listOf(Subjects, CsYears, CsMap) is CsLesson -> listOf(Subjects, CsYears, CsMap, this) @@ -338,6 +364,11 @@ fun routeFromPage(page: String): Route? = when (page) { "tg2map" -> Route.Tg2Map "tg3map" -> Route.Tg3Map "tg4map" -> Route.Tg4Map + "pgyears" -> Route.PgYears + "pgmap" -> Route.PgMap + "pg2map" -> Route.Pg2Map + "pg3map" -> Route.Pg3Map + "pg4map" -> Route.Pg4Map "csyears" -> Route.CsYears "csmap" -> Route.CsMap "cs2map" -> Route.Cs2Map @@ -464,6 +495,30 @@ fun routeFromPage(page: String): Route? = when (page) { Regex("""tgex(\d+)""").matchEntire(page)?.let { return Route.TgEx(it.groupValues[1].toInt()) } + Regex("""pg4unit(\d+)""").matchEntire(page)?.let { + return Route.Pg4Lesson(it.groupValues[1].toInt()) + } + Regex("""pg4ex(\d+)""").matchEntire(page)?.let { + return Route.Pg4Ex(it.groupValues[1].toInt()) + } + Regex("""pg3unit(\d+)""").matchEntire(page)?.let { + return Route.Pg3Lesson(it.groupValues[1].toInt()) + } + Regex("""pg3ex(\d+)""").matchEntire(page)?.let { + return Route.Pg3Ex(it.groupValues[1].toInt()) + } + Regex("""pg2unit(\d+)""").matchEntire(page)?.let { + return Route.Pg2Lesson(it.groupValues[1].toInt()) + } + Regex("""pg2ex(\d+)""").matchEntire(page)?.let { + return Route.Pg2Ex(it.groupValues[1].toInt()) + } + Regex("""pgunit(\d+)""").matchEntire(page)?.let { + return Route.PgLesson(it.groupValues[1].toInt()) + } + Regex("""pgex(\d+)""").matchEntire(page)?.let { + return Route.PgEx(it.groupValues[1].toInt()) + } Regex("""cs4unit(\d+)""").matchEntire(page)?.let { return Route.Cs4Lesson(it.groupValues[1].toInt()) } diff --git a/android/shared/src/org/maturita/maturita/data/Content.kt b/android/shared/src/org/maturita/maturita/data/Content.kt index f48e8ae..802a9a3 100644 --- a/android/shared/src/org/maturita/maturita/data/Content.kt +++ b/android/shared/src/org/maturita/maturita/data/Content.kt @@ -60,6 +60,10 @@ class Content(root: JSONObject) { val tg2 = raw.arr("tg2") val tg3 = raw.arr("tg3") val tg4 = raw.arr("tg4") + val pg = raw.arr("pg") + val pg2 = raw.arr("pg2") + val pg3 = raw.arr("pg3") + val pg4 = raw.arr("pg4") val cs = raw.arr("cs") val cs2 = raw.arr("cs2") val cs3 = raw.arr("cs3") @@ -120,6 +124,10 @@ class Content(root: JSONObject) { fun tg2Lesson(id: Int) = tg2.firstOrNull { it.int("id") == id } fun tg3Lesson(id: Int) = tg3.firstOrNull { it.int("id") == id } fun tg4Lesson(id: Int) = tg4.firstOrNull { it.int("id") == id } + fun pgLesson(id: Int) = pg.firstOrNull { it.int("id") == id } + fun pg2Lesson(id: Int) = pg2.firstOrNull { it.int("id") == id } + fun pg3Lesson(id: Int) = pg3.firstOrNull { it.int("id") == id } + fun pg4Lesson(id: Int) = pg4.firstOrNull { it.int("id") == id } fun csLesson(id: Int) = cs.firstOrNull { it.int("id") == id } fun cs2Lesson(id: Int) = cs2.firstOrNull { it.int("id") == id } fun cs3Lesson(id: Int) = cs3.firstOrNull { it.int("id") == id } diff --git a/android/shared/src/org/maturita/maturita/data/ProgressStore.kt b/android/shared/src/org/maturita/maturita/data/ProgressStore.kt index df6c24a..7dd417c 100644 --- a/android/shared/src/org/maturita/maturita/data/ProgressStore.kt +++ b/android/shared/src/org/maturita/maturita/data/ProgressStore.kt @@ -99,6 +99,14 @@ class ProgressStore(private val prefs: Prefs, content: Content) { fun markTg3(id: Int) { mark("tg3.$id") } fun tg4Done(id: Int) = done("tg4.$id") fun markTg4(id: Int) { mark("tg4.$id") } + fun pgDone(id: Int) = done("pg.$id") + fun markPg(id: Int) { mark("pg.$id") } + fun pg2Done(id: Int) = done("pg2.$id") + fun markPg2(id: Int) { mark("pg2.$id") } + fun pg3Done(id: Int) = done("pg3.$id") + fun markPg3(id: Int) { mark("pg3.$id") } + fun pg4Done(id: Int) = done("pg4.$id") + fun markPg4(id: Int) { mark("pg4.$id") } fun csDone(id: Int) = done("cs.$id") fun markCs(id: Int) { mark("cs.$id") } fun cs2Done(id: Int) = done("cs2.$id") @@ -328,6 +336,38 @@ fun summarize(content: Content, p: ProgressStore): ProgressSum { sum.doneUnits += 1 } } + content.pg.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.pgDone(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.pg2.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.pg2Done(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.pg3.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.pg3Done(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.pg4.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.pg4Done(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } content.cs.forEach { l -> sum.totalEx += 1 sum.openUnits += 1 diff --git a/android/shared/src/org/maturita/maturita/ui/Exercises.kt b/android/shared/src/org/maturita/maturita/ui/Exercises.kt index 8fcddd6..8343011 100644 --- a/android/shared/src/org/maturita/maturita/ui/Exercises.kt +++ b/android/shared/src/org/maturita/maturita/ui/Exercises.kt @@ -1106,6 +1106,30 @@ fun TgQuizScreen(vm: AppViewModel, year: Int, id: Int) { } } +@Composable +fun PgQuizScreen(vm: AppViewModel, year: Int, id: Int) { + val lesson = when (year) { + 2 -> vm.content.pg2Lesson(id) + 3 -> vm.content.pg3Lesson(id) + 4 -> vm.content.pg4Lesson(id) + else -> vm.content.pgLesson(id) + } ?: return + val spec = J(org.json.JSONObject().apply { + put("type", "choice") + put("title", vm.tr(lesson.str("exTitleKey"))) + put("sub", lesson.str("quizHeadKey")) + put("questions", lesson.o.optJSONArray("quiz")) + }) + DispatchExercise(vm, spec, vm.tr(lesson.str("exTitleKey"))) { + when (year) { + 2 -> vm.markPg2(id) + 3 -> vm.markPg3(id) + 4 -> vm.markPg4(id) + else -> vm.markPg(id) + } + } +} + @Composable fun FyzQuizScreen(vm: AppViewModel, year: Int, id: Int) { val lesson = when (year) { diff --git a/android/shared/src/org/maturita/maturita/ui/Screens.kt b/android/shared/src/org/maturita/maturita/ui/Screens.kt index 39404c3..afd4624 100644 --- a/android/shared/src/org/maturita/maturita/ui/Screens.kt +++ b/android/shared/src/org/maturita/maturita/ui/Screens.kt @@ -29,6 +29,7 @@ import androidx.compose.material.icons.filled.Architecture import androidx.compose.material.icons.filled.AutoAwesome import androidx.compose.material.icons.filled.AutoStories import androidx.compose.material.icons.filled.Bolt +import androidx.compose.material.icons.filled.Brush import androidx.compose.material.icons.filled.Eco import androidx.compose.material.icons.filled.Functions import androidx.compose.material.icons.filled.Check @@ -246,6 +247,7 @@ private fun SubjectIcon(icon: String, open: Boolean) { "function" -> Icon(Icons.Filled.Functions, null, tint = tint, modifier = Modifier.size(26.dp)) "ruler" -> Icon(Icons.Filled.Architecture, null, tint = tint, modifier = Modifier.size(26.dp)) "code" -> Icon(Icons.Filled.Terminal, null, tint = tint, modifier = Modifier.size(26.dp)) + "brush" -> Icon(Icons.Filled.Brush, null, tint = tint, modifier = Modifier.size(26.dp)) else -> Icon(Icons.Filled.Lock, null, tint = tint) } } @@ -363,6 +365,10 @@ fun routeProgress(vm: AppViewModel, r: Route): Pair? { Route.Tg2Map -> tally(c.tg2, p::tg2Done) Route.Tg3Map -> tally(c.tg3, p::tg3Done) Route.Tg4Map -> tally(c.tg4, p::tg4Done) + Route.PgMap -> tally(c.pg, p::pgDone) + Route.Pg2Map -> tally(c.pg2, p::pg2Done) + Route.Pg3Map -> tally(c.pg3, p::pg3Done) + Route.Pg4Map -> tally(c.pg4, p::pg4Done) Route.CsMap -> tally(c.cs, p::csDone) Route.Cs2Map -> tally(c.cs2, p::cs2Done) Route.Cs3Map -> tally(c.cs3, p::cs3Done) @@ -444,6 +450,13 @@ private fun subjectSum(vm: AppViewModel, s: J): ProgressSum { vm.content.tg3.count { vm.progress.tg3Done(it.int("id")) } + vm.content.tg4.count { vm.progress.tg4Done(it.int("id")) } } + "pgyears", "pgmap", "pg2map", "pg3map", "pg4map" -> { + sum.totalEx = vm.content.pg.size + vm.content.pg2.size + vm.content.pg3.size + vm.content.pg4.size + sum.doneEx = vm.content.pg.count { vm.progress.pgDone(it.int("id")) } + + vm.content.pg2.count { vm.progress.pg2Done(it.int("id")) } + + vm.content.pg3.count { vm.progress.pg3Done(it.int("id")) } + + vm.content.pg4.count { vm.progress.pg4Done(it.int("id")) } + } "czechmap" -> { val litDone = vm.content.lit.count { vm.progress.litDone(it.int("id")) } + vm.content.lit2.count { vm.progress.lit2Done(it.int("id")) } + @@ -745,6 +758,27 @@ private fun buildSearch(vm: AppViewModel): List { add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "fyz4unit${l.int("id")}", extra = "fyzika") add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "fyz4ex${l.int("id")}") } + add(vm.tr("Prezentační grafika"), vm.tr("search_lesson"), "pgyears", extra = "photoshop premiere illustrator") + add(vm.tr("pg_year1"), vm.tr("search_lesson"), "pgmap", extra = "rocnik") + add(vm.tr("pg_year2"), vm.tr("search_lesson"), "pg2map", extra = "rocnik") + add(vm.tr("pg_year3"), vm.tr("search_lesson"), "pg3map", extra = "rocnik") + add(vm.tr("pg_year4"), vm.tr("search_lesson"), "pg4map", extra = "rocnik") + vm.content.pg.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "pgunit${l.int("id")}", extra = "photoshop") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "pgex${l.int("id")}") + } + vm.content.pg2.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "pg2unit${l.int("id")}", extra = "photoshop") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "pg2ex${l.int("id")}") + } + vm.content.pg3.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "pg3unit${l.int("id")}", extra = "premiere") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "pg3ex${l.int("id")}") + } + vm.content.pg4.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "pg4unit${l.int("id")}", extra = "illustrator") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "pg4ex${l.int("id")}") + } add(vm.tr("Technická grafika"), vm.tr("search_lesson"), "tgyears", extra = "autocad grafika") add(vm.tr("tg_year1"), vm.tr("search_lesson"), "tgmap", extra = "rocnik") add(vm.tr("tg_year2"), vm.tr("search_lesson"), "tg2map", extra = "rocnik") @@ -998,6 +1032,20 @@ fun CsYearsScreen(vm: AppViewModel) { } } +@Composable +fun PgYearsScreen(vm: AppViewModel) { + DetailScaffold(vm, vm.tr("Prezentační grafika"), vm.tr("pg_years_sub")) { inner -> + LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { + item { + NavRow(title = vm.tr("pg_year1"), subtitle = vm.tr("pg_sub"), leading = { LeadingBadge("1") }, progress = routeProgress(vm, Route.PgMap)) { vm.go(Route.PgMap) } + NavRow(title = vm.tr("pg_year2"), subtitle = vm.tr("pg2_sub"), leading = { LeadingBadge("2") }, progress = routeProgress(vm, Route.Pg2Map)) { vm.go(Route.Pg2Map) } + NavRow(title = vm.tr("pg_year3"), subtitle = vm.tr("pg3_sub"), leading = { LeadingBadge("3") }, progress = routeProgress(vm, Route.Pg3Map)) { vm.go(Route.Pg3Map) } + NavRow(title = vm.tr("pg_year4"), subtitle = vm.tr("pg4_sub"), leading = { LeadingBadge("4") }, progress = routeProgress(vm, Route.Pg4Map)) { vm.go(Route.Pg4Map) } + } + } + } +} + @Composable fun TgYearsScreen(vm: AppViewModel) { DetailScaffold(vm, vm.tr("Technická grafika"), vm.tr("tg_years_sub")) { inner -> diff --git a/content/README.md b/content/README.md index f61e3a9..dfa12a4 100644 --- a/content/README.md +++ b/content/README.md @@ -18,7 +18,7 @@ Skript vypíše počet lekcí, nebo chyby s cestou a číslem řádku | Složka | Obsah | |---|---| -| `pocitacove-site/`, `technicke-vybaveni/`, `technicka-grafika/`, `programovani/`, `obcanska-nauka/`, `fyzika/` | `N-rocnik/`, v nich lekce | +| `pocitacove-site/`, `technicke-vybaveni/`, `technicka-grafika/`, `prezentacni-grafika/`, `programovani/`, `obcanska-nauka/`, `fyzika/` | `N-rocnik/`, v nich lekce | | `prirodni-vedy/chemie`, `prirodni-vedy/biologie` | lekce | | `cestina/literatura/N-rocnik` | lekce literatury | | `cestina/mluvnice` | cvičení z mluvnice | @@ -97,7 +97,7 @@ a smažte `puvodni`. ## Běžná lekce -Sítě, hardware, technická grafika, občanka, literatura, chemie, biologie, fyzika. +Sítě, hardware, technická grafika, prezentační grafika, občanka, literatura, chemie, biologie, fyzika. ```md --- diff --git a/content/aplikace/novinky.txt b/content/aplikace/novinky.txt index 827a841..280d4c3 100644 --- a/content/aplikace/novinky.txt +++ b/content/aplikace/novinky.txt @@ -5,6 +5,12 @@ # cs: bullet # en: bullet +--- 2026-10-11 +cs: Prezentační grafika: Photoshop, Premiere Pro a další programy Adobe +cs: Čtyři ročníky, v každé lekci postup, zadání a kvíz +en: Presentation graphics: Photoshop, Premiere Pro and the other Adobe apps +en: Four years, and every lesson has a method, an assignment and a quiz + --- 2026-10-11 cs: Programování: čtyři ročníky C#, od výpisu po LINQ a async cs: Kód se píše v lekci a hned vedle se spustí diff --git a/content/aplikace/predmety.md b/content/aplikace/predmety.md index 9ecbd76..d55c8ec 100644 --- a/content/aplikace/predmety.md +++ b/content/aplikace/predmety.md @@ -12,6 +12,6 @@ | Fyzika | Fyzika | Physics | bolt | fyzyears | ano | | Základy přírodních věd | Základy přírodních věd | Fundamentals of Natural Sciences | flask | scimap | ano | | Technická grafika | Technická grafika | Technical Graphics | ruler | tgyears | ano | -| Prezentační grafika | Prezentační grafika | Presentation Graphics | lock | | ne | +| Prezentační grafika | Prezentační grafika | Presentation Graphics | brush | pgyears | ano | | Programování | Programování | Programming | code | csyears | ano | | Programové vybavení | Programové vybavení | Software | lock | | ne | diff --git a/content/aplikace/texty.md b/content/aplikace/texty.md index 34e1048..5177ae8 100644 --- a/content/aplikace/texty.md +++ b/content/aplikace/texty.md @@ -244,6 +244,20 @@ | tg3_sub | Pohledy, řezy, bloky a tisk. | Views, sections, blocks and plotting. | | tg4_sub | 3D model a výkres k maturitě. | A 3D model and the exam drawing. | +# Prezentační grafika + +| Klíč | Česky | Anglicky | +|---|---|---| +| pg_years_sub | Od Photoshopu přes Premiere Pro k Illustratoru, After Effects a InDesignu. | From Photoshop through Premiere Pro to Illustrator, After Effects and InDesign. | +| pg_year1 | 1. ročník | Year 1 | +| pg_year2 | 2. ročník | Year 2 | +| pg_year3 | 3. ročník | Year 3 | +| pg_year4 | 4. ročník | Year 4 | +| pg_sub | Photoshop: pixely, vrstvy, výběr a retuš. | Photoshop: pixels, layers, selections and retouching. | +| pg2_sub | Masky, barvy, plakát a export. | Masks, colour, a poster and export. | +| pg3_sub | Premiere Pro: střih, zvuk, titulky a video. | Premiere Pro: the cut, sound, titles and the video. | +| pg4_sub | Illustrator, After Effects, InDesign a maturitní kampaň. | Illustrator, After Effects, InDesign and the exam campaign. | + # Programování | Klíč | Česky | Anglicky | diff --git a/content/prezentacni-grafika/1-rocnik/01-bitmapa-a-vektor.en.md b/content/prezentacni-grafika/1-rocnik/01-bitmapa-a-vektor.en.md new file mode 100644 index 0000000..2e28f6f --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/01-bitmapa-a-vektor.en.md @@ -0,0 +1,6 @@ +--- +nazev: Bitmap and vector +popis: When a picture is pixels and when it is curves. +nazev_cviceni: Quiz: bitmap and vector +nadpis_kvizu: Tell what goes soft when you enlarge it +--- diff --git a/content/prezentacni-grafika/1-rocnik/01-bitmapa-a-vektor.md b/content/prezentacni-grafika/1-rocnik/01-bitmapa-a-vektor.md new file mode 100644 index 0000000..167b175 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/01-bitmapa-a-vektor.md @@ -0,0 +1,79 @@ +--- +id: pg-1-bitmapa-a-vektor +nazev: Bitmapa a vektor +popis: Kdy je obrázek z pixelů a kdy z křivek. +nazev_cviceni: Kvíz: bitmapa a vektor +nadpis_kvizu: Poznejte, co se při zvětšení rozmaže +--- + +# Výklad + +## Dva druhy obrázku +*Pixel a křivka* + +- Bitmapa je mřížka pixelů. Fotka z telefonu je bitmapa a otevírá se ve Photoshopu. +- Vektor je popis křivek. Logo a ikona se kreslí v Illustratoru a při zvětšení zůstávají ostré. +- Photoshop umí obojí, ale fotku v něm nepřekreslíte na křivky. Naopak Illustrator není na retuš. +- Když nevíte, který soubor je který, přibližte ho na 400 %. Bitmapa ukáže čtverečky. + +> Tip: Přípona napoví: JPG a PNG jsou bitmapa, SVG a AI jsou vektor. PSD může obsahovat obojí. + +## Co se stane při zvětšení +*Kvalita* + +- Bitmapu 800 px širokou nelze vytisknout ostře na velký plakát. Pixely se jen roztáhnou. +- Vektor se zvětší přepočtem křivky. Hranu má pořád hladkou. +- Zmenšení bitmapy pixely zahodí. Zvětšení zpět je nevrátí. +- Proto se logo neukládá jako malé JPG a fotka se nefotí až ve chvíli, kdy už je pozdě. + +> Tip: Jednou zmenšenou fotku už nedostanete zpátky. Nechte si původní soubor. + +## Který program kdy +*Volba* + +- Fotku, retuš a plakát z fotografie berte do Photoshopu. +- Logo, piktogram a čisté tvary berte do Illustratoru. Ten přijde ve čtvrtém ročníku. +- Video nepatří ani do jednoho. Na střih je Premiere Pro, ve třeťáku. +- Hotové logo z Illustratoru se do Photoshopu vloží jako chytrý objekt, ne jako malý JPG. + +> Tip: Chytrý objekt drží původní kvalitu. ZJPG-ované logo už křivky nemá. + +## Zadání: porovnání +*Cvičení* + +- Otevřete ve Photoshopu fotku a přibližte ji na 400 %. Okraj předmětu se rozpadne na pixely. +- Stejný pohled udělejte u loga ve formátu SVG nebo AI. Hrana zůstane hladká. +- Do sešitu napište, který soubor je bitmapa a který vektor, a proč. +- Fotku uložte jako porovnani.psd. Logo nepřevádějte na JPG. + +> Tip: Když SVG ve Photoshopu rastruje, zaškrtněte velký rozměr. Malý náhled nic neukáže. + +# Kvíz + +## Co uvidíte na fotce při zvětšení na 400 %? +- Hladkou křivku bez pixelů +- [x] Čtverečky pixelů +- Jen ořezové značky +- Prázdné plátno +> Fotka je bitmapa. Zvětšení roztáhne pixely, křivku z ní neudělá. + +## Kam patří nové logo školy? +- Do Photoshopu jako malé JPG +- [x] Do Illustratoru jako křivky +- Do Premiere jako titulek +- Do JPG o šířce 80 px +> Logo se zvětšuje na plakát i na vizitku. Křivka to zvládne, malé JPG ne. + +## Co se stane, když bitmapu zmenšíte a pak znovu zvětšíte? +- Pixely se vrátí +- [x] Ostrost se ztratí +- Soubor se stane vektorem +- Přibude spadávka +> Zmenšení pixely zahodí. Zvětšení je neumí vymyslet zpět. + +## Jak se má logo vložit do plakátu ve Photoshopu? +- Jako JPG 80 px +- [x] Jako chytrý objekt +- Vložením snímku obrazovky +- Jako zvuková stopa +> Chytrý objekt podrží zdroj. JPG logo už nejde zvětšit bez rozmazání. diff --git a/content/prezentacni-grafika/1-rocnik/02-rozliseni-a-barva.en.md b/content/prezentacni-grafika/1-rocnik/02-rozliseni-a-barva.en.md new file mode 100644 index 0000000..902f025 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/02-rozliseni-a-barva.en.md @@ -0,0 +1,6 @@ +--- +nazev: Resolution and colour +popis: Pixels, 300 ppi for print, and RGB against CMYK. +nazev_cviceni: Quiz: resolution and colour +nadpis_kvizu: Pick the size and the colour mode +--- diff --git a/content/prezentacni-grafika/1-rocnik/02-rozliseni-a-barva.md b/content/prezentacni-grafika/1-rocnik/02-rozliseni-a-barva.md new file mode 100644 index 0000000..86b6f23 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/02-rozliseni-a-barva.md @@ -0,0 +1,79 @@ +--- +id: pg-1-rozliseni-a-barva +nazev: Rozlišení a barva +popis: Pixely, tisk 300 ppi a rozdíl mezi RGB a CMYK. +nazev_cviceni: Kvíz: rozlišení a barva +nadpis_kvizu: Vyberte rozměr a barevný režim +--- + +# Výklad + +## Pixely a tisk +*Rozlišení* + +- Rozměr v pixelech říká, kolik bodů obrázek má. 1920 × 1080 je Full HD. +- Rozlišení v ppi říká, kolik pixelů připadne na palec při tisku. +- Na obrazovku stačí 72 ppi. Do tisku se připravuje 300 ppi. +- Formát A4 při 300 ppi má zhruba 2480 × 3508 px. To je 210 × 297 mm. + +> Tip: 72 ppi na plakát nestačí. 300 ppi na web je zbytečně velký soubor. + +## RGB a CMYK +*Režim* + +- RGB je světlo obrazovky: červená, zelená a modrá. Web i video jsou RGB. +- CMYK jsou tiskové barvy: azurová, purpurová, žlutá a černá. +- Barva, která svítí na monitoru, se v tisku často smrskne. Základní červená v CMYK zešedne. +- Fotku upravujte v RGB. Do CMYK ji převádějte až při exportu pro tiskárnu. + +> Tip: Když tiskárna chce PDF v CMYK, převod nechte na export. Pracovní soubor zůstane RGB. + +## Nový dokument +*Plátno* + +- Webový obrázek: 1920 × 1080 px, 72 ppi, RGB, 8 bitů na kanál. +- Tiskové A4: předvolba A4, 300 ppi, RGB. Ořez řešte až u plakátu ve druháku. +- Pozadí nové školní práce nechte průhledné, ať pod ním není bílá omylem. +- Pojmenujte dokument hned. nazev-skoly.psd je lepší než Bez názvu-1. + +> Tip: 8 bitů na kanál je běžná fotka. 16 bitů nechte na složitou retuš, soubor je větší. + +## Zadání: dva dokumenty +*Cvičení* + +- Založte web.psd: 1920 × 1080 px, 72 ppi, RGB, 8 bitů. +- Založte tisk-a4.psd: A4, 300 ppi, RGB, 8 bitů. +- U obou zapište do vrstvy poznámku s rozměrem v pixelech. U A4 to má být kolem 2480 × 3508. +- Oba soubory uložte jako PSD. Zatím je neexportujte do JPG. + +> Tip: Obraz > Velikost obrazu ukáže pixely i centimetry. Jednotky si přepněte, ať čtete obojí. + +# Kvíz + +## Jaké rozlišení se bere pro tisk? +- 72 ppi +- [x] 300 ppi +- 25 sn/s +- 8 bitů +> 72 ppi je obrazovka. Tisk chce 300 ppi, jinak je fotka na papíře měkká. + +## Který režim patří na web a do videa? +- CMYK +- [x] RGB +- Jen černá +- Spadávka +> Obrazovka skládá barvu ze světla RGB. CMYK je pro tiskárnu. + +## Kolik pixelů má zhruba A4 při 300 ppi? +- 1920 × 1080 +- [x] 2480 × 3508 +- 210 × 297 +- 800 × 600 +> 210 × 297 mm je rozměr papíru. V pixelech při 300 ppi je to asi 2480 × 3508. + +## Kdy se má fotka převést do CMYK? +- Hned po otevření +- [x] Až při exportu pro tisk +- Před každou retuší +- Nikdy, ani pro tisk +> Úpravy nechte v RGB. CMYK je výstup pro tiskárnu, ne pracovní soubor. diff --git a/content/prezentacni-grafika/1-rocnik/03-prostredi-photoshopu.en.md b/content/prezentacni-grafika/1-rocnik/03-prostredi-photoshopu.en.md new file mode 100644 index 0000000..3f0a246 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/03-prostredi-photoshopu.en.md @@ -0,0 +1,6 @@ +--- +nazev: The Photoshop workspace +popis: Panels, zoom and saving a PSD. +nazev_cviceni: Quiz: the workspace +nadpis_kvizu: Find the shortcut and the right file +--- diff --git a/content/prezentacni-grafika/1-rocnik/03-prostredi-photoshopu.md b/content/prezentacni-grafika/1-rocnik/03-prostredi-photoshopu.md new file mode 100644 index 0000000..a2bcd79 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/03-prostredi-photoshopu.md @@ -0,0 +1,79 @@ +--- +id: pg-1-prostredi +nazev: Prostředí Photoshopu +popis: Panely, lupa a ukládání PSD. +nazev_cviceni: Kvíz: prostředí +nadpis_kvizu: Najděte zkratku a správný soubor +--- + +# Výklad + +## Okno +*Panely* + +- Vlevo je panel nástrojů. Nahoře jsou volby vybraného nástroje, mění se s ním. +- Vpravo jsou Vrstvy, Barvy a Vlastnosti. Když panel zmizí, vrátí ho Okno a název panelu. +- Uprostřed je plátno. Šedé okolí není součást obrázku, netiskne se. +- Pracovní plochu Fotografie nebo Základní nastavíte vpravo nahoře. Cizí rozložení si neukládejte přes cizí účet. + +> Tip: Na Macu je Ctrl většinou Cmd. Alt je Option. Zkratky v lekcích jsou windowsové. + +## Pohyb po obraze +*Lupa* + +- Mezerník dočasně vezme ručičku a obraz se posouvá. +- Ctrl+0 vměstná celé plátno do okna. Ctrl+1 ukáže 100 %, jeden pixel na pixel. +- Nástroj lupa je Z. Alt a klik zmenšuje. +- Při retuši kontrolujte 100 %. Na celém plátně drobná chyba zanikne. + +> Tip: Ctrl+Alt+Z krokne zpět o víc než jeden krok. Panel Historie ukáže, kam až se dá vrátit. + +## Uložení +*PSD a JPG* + +- Pracovní soubor je PSD. Drží vrstvy, masky a text. +- JPG je kopie na odevzdání nebo na web. Vrstvy v něm nejsou. +- Soubor > Uložit jako nechá PSD. Export > Exportovat jako udělá JPG nebo PNG. +- JPG kvalitu 10 (nebo 80 %) berte na fotku. Nižší kvalita dělá kostky. + +> Tip: Jednou uložené JPG znovu neukládejte jako jediný zdroj. Každý export ubere detail. + +## Zadání: první soubor +*Cvičení* + +- Otevřete web.psd z minulé lekce, nebo založte nový 1920 × 1080 px. +- Přejmenujte první vrstvu na pozadi a uložte soubor jako uvod.psd. +- Ctrl+0 a Ctrl+1 si vyzkoušejte. Na 100 % má být vidět pixel, ne celé plátno. +- Exportujte kopii uvod.jpg v kvalitě 10. PSD si nechte. + +> Tip: Když Exportovat jako nabídne PNG, přepněte formát na JPG. PNG tu ještě nepotřebujeme. + +# Kvíz + +## Co udělá Ctrl+1? +- Vloží novou vrstvu +- [x] Ukáže obraz v měřítku 100 % +- Smaže plátno +- Převede barvy do CMYK +> 100 % je jeden pixel souboru na jeden pixel obrazovky. Ctrl+0 naopak vměstná celé plátno. + +## Který soubor si necháváte jako pracovní? +- JPG +- [x] PSD +- MP4 +- Jen snímek obrazovky +> PSD drží vrstvy. JPG je plochá kopie. + +## Čím se při přiblížení posouvá obraz? +- Nástrojem text +- [x] Mezerníkem +- Klávesou D +- Exportem +> Mezerník dočasně přepne na ručičku, ať nemusíte měnit nástroj. + +## Jakou kvalitu JPG berete na obyčejnou fotku? +- 1 +- [x] 10, tedy kolem 80 % +- JPG se na fotku nehodí +- Jen PNG +> Kvalita 1 obraz rozbije na kostky. Kolem 10 je rozumný kompromis velikosti a vzhledu. diff --git a/content/prezentacni-grafika/1-rocnik/04-vrstvy.en.md b/content/prezentacni-grafika/1-rocnik/04-vrstvy.en.md new file mode 100644 index 0000000..468d0a9 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/04-vrstvy.en.md @@ -0,0 +1,6 @@ +--- +nazev: Layers +popis: Order, opacity and groups. An edit does not sit on the photo pixels. +nazev_cviceni: Quiz: layers +nadpis_kvizu: Put the layers in the right order +--- diff --git a/content/prezentacni-grafika/1-rocnik/04-vrstvy.md b/content/prezentacni-grafika/1-rocnik/04-vrstvy.md new file mode 100644 index 0000000..25253f5 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/04-vrstvy.md @@ -0,0 +1,79 @@ +--- +id: pg-1-vrstvy +nazev: Vrstvy +popis: Pořadí, krytí a skupiny. Úprava nesedí na pixelech fotky. +nazev_cviceni: Kvíz: vrstvy +nadpis_kvizu: Dejte vrstvy do správného pořadí +--- + +# Výklad + +## Proč vrstva +*Pořádek* + +- Každá úprava má vlastní vrstvu. Fotka vespod zůstane nedotčená. +- Horní vrstva překrývá spodní. Text patří nahoru, fotka dolů. +- Oko u vrstvy ji skryje. Zámek zabrání posunu. +- Vrstvu pojmenujte: fotka, retus, nadpis. Název Vrstva 1 nic neřekne. + +> Tip: Když smažete vrstvu, fotka pod ní zůstane. Když malujete přímo do fotky, už ji neoddělíte. + +## Krytí a pořadí +*Vzhled* + +- Krytí 100 % je plná vrstva. Krytí 70 % nechá prosvítat to, co je pod ní. +- Pořadí změníte tažením v panelu Vrstvy. +- Režim prolnutí nechte na Normální, dokud ho lekce výslovně nezmění. +- Jedna věc na jedné vrstvě. Nadpis a stín textu k sobě patří, retuš ne. + +> Tip: Krytí je u horního okraje panelu Vrstvy. Není to totéž co Výplň, tu zatím nechte 100 %. + +## Skupina +*Složka* + +- Ctrl+G vloží vybrané vrstvy do skupiny. Skupina je složka v panelu. +- Skupina Popisek ať drží nadpis i podnadpis. Skupina Foto ať drží fotku a retuš. +- Skrytí skupiny skryje všechno v ní. Jednotlivé vrstvy tím nesmažete. +- Rozdělit skupinu jde Ctrl+Shift+G. + +> Tip: Nejdřív vyberte vrstvy se Shiftem, pak Ctrl+G. Prázdná skupina sama nic nesrovná. + +## Zadání: tři patra +*Cvičení* + +- V uvod.psd mějte dole vrstvu fotka. +- Nad ní vrstvu uprav, krytí 70 %. Zatím na ni stačí jednobarevný obdélník přes spodní třetinu. +- Úplně nahoru vrstvu nadpis s textem Školní den. +- Vrstvy nadpis a uprav dejte do skupiny popisek. Uložte PSD. + +> Tip: Když text nejde přečíst, není špatně písmo, ale pořadí. Nadpis nesmí skončit pod fotkou. + +# Kvíz + +## Kam patří vrstva s fotkou? +- Úplně nahoru +- [x] Dolu, pod úpravy a text +- Mimo panel Vrstvy +- Do JPG +> Fotka je podklad. Všechno, co se má dát vypnout, leží nad ní. + +## Co znamená krytí 70 %? +- Vrstva je o 70 px menší +- [x] Vrstva je částečně průhledná +- Text má 70 bodů +- Soubor má 70 ppi +> 100 % je plná vrstva. Nižší krytí nechá prosvítat vrstvy pod ní. + +## Čím se vybrané vrstvy seskupí? +- Ctrl+1 +- [x] Ctrl+G +- Klávesou D +- Exportem do JPG +> Ctrl+G udělá skupinu. Ctrl+Shift+G ji zase rozebere. + +## Proč se nemaluje přímo do vrstvy fotky? +- Photoshop to zakazuje +- [x] Úprava pak nejde oddělit ani vypnout +- Fotka by se stala vektorem +- Změní se to na CMYK +> Vlastní vrstvu smažete nebo skryjete. Pixely fotky už od malby neočistíte. diff --git a/content/prezentacni-grafika/1-rocnik/05-vybery.en.md b/content/prezentacni-grafika/1-rocnik/05-vybery.en.md new file mode 100644 index 0000000..fa401db --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/05-vybery.en.md @@ -0,0 +1,6 @@ +--- +nazev: Selections +popis: Mark part of the picture and copy it to a new layer. +nazev_cviceni: Quiz: selections +nadpis_kvizu: Pick the tool and the shortcut +--- diff --git a/content/prezentacni-grafika/1-rocnik/05-vybery.md b/content/prezentacni-grafika/1-rocnik/05-vybery.md new file mode 100644 index 0000000..7d42859 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/05-vybery.md @@ -0,0 +1,79 @@ +--- +id: pg-1-vybery +nazev: Výběry +popis: Označení části obrazu a kopie na novou vrstvu. +nazev_cviceni: Kvíz: výběry +nadpis_kvizu: Vyberte nástroj a zkratku +--- + +# Výklad + +## Nástroje výběru +*Tvar* + +- Obdélníkový a eliptický výběr řežou pravidelný tvar. Hodí se na ořez a banner. +- Laso obkreslí nepravidelný okraj rukou. Na vlasy je nepřesné. +- Rychlý výběr a předmět malují přes plochu, kterou chcete. Photoshop dopočítá hranu. +- Kouzelná hůlka bere souvislou podobnou barvu. Na oblohu ano, na tvář ne. + +> Tip: Shift k výběru přidává, Alt ubírá. Ve volbách nástroje je totéž jako ikony. + +## Hrana +*Prolnutí* + +- Tvrdý výběr dělá nalepený obrys. Prolnutí okraje (feather) hranu změkčí. +- U postavy stačí prolnutí 1 až 2 px. U oblohy může být větší. +- Prolnutí se nastavuje před potvrzením, nebo přes Výběr > Upravit > Prolnout okraj. +- Když je kolem vlasů barevný lem, výběr byl moc hrubý. Zpřesněte ho, neschovávejte ho rozmazáním. + +> Tip: Prolnutí 20 px z člověka udělá ducha. Na výřez postavy je to moc. + +## Nová vrstva z výběru +*Ctrl+J* + +- Ctrl+J zkopíruje výběr na novou vrstvu. Původní fotka zůstane vespod. +- Tak se postava oddělí od pozadí, aniž byste pozadí mazali. +- Výběr zrušíte Ctrl+D. Když ho nezrušíte, další malování se ořeže jen do něj. +- Vrstvu s postavou pojmenujte osoba. Vrstvu pod ní pozadi. + +> Tip: Ctrl+J bez výběru zkopíruje celou vrstvu. Nejdřív musí běžet mravenci kolem výběru. + +## Zadání: osoba bokem +*Cvičení* + +- Vyberte postavu rychlým výběrem. Okraj doplňte se Shiftem a Alt. +- Dejte prolnutí 2 px a Ctrl+J. Vznikne vrstva osoba. +- Vrstvu pozadi ztmavte tak, aby šla vypnout. Postava zůstane ve své vrstvě světlá. +- Uložte vyber.psd. Výběr před uložením zrušte Ctrl+D. + +> Tip: Když ztmavení sedí i na postavě, malovali jste do špatné vrstvy. Ztmavení patří jen na pozadi. + +# Kvíz + +## Který nástroj bere souvislou podobnou barvu, třeba oblohu? +- Text +- [x] Kouzelná hůlka +- Ořez +- Pero v Illustratoru +> Hůlka skočí po podobné barvě. Na tvář, která má víc odstínů, se nehodí. + +## Co udělá Ctrl+J, když je něco vybrané? +- Smaže výběr +- [x] Zkopíruje výběr na novou vrstvu +- Převede obraz do CMYK +- Založí video +> Původní vrstva zůstane. Nová obsahuje jen vybranou část. + +## Jaké prolnutí okraje dává smysl u postavy? +- [x] 2 px +- 80 px +- 300 ppi +- Žádné, ale 40 px stín na celé fotce +> 1 až 2 px hranu změkčí. Desítky pixelů postavu rozmažou. + +## Čím se výběr zruší? +- [x] Ctrl+D +- Ctrl+0 +- Exportem JPG +- Krytím 70 % +> Nezrušený výběr omezí další malování jen dovnitř mravenců. diff --git a/content/prezentacni-grafika/1-rocnik/06-retus.en.md b/content/prezentacni-grafika/1-rocnik/06-retus.en.md new file mode 100644 index 0000000..6ff7911 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/06-retus.en.md @@ -0,0 +1,6 @@ +--- +nazev: Retouching +popis: The clone stamp and the healing brush on their own layer. +nazev_cviceni: Quiz: retouching +nadpis_kvizu: Pick the tool and the layer +--- diff --git a/content/prezentacni-grafika/1-rocnik/06-retus.md b/content/prezentacni-grafika/1-rocnik/06-retus.md new file mode 100644 index 0000000..e7def42 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/06-retus.md @@ -0,0 +1,79 @@ +--- +id: pg-1-retus +nazev: Retuš +popis: Klonovací razítko a opravný štětec na vlastní vrstvě. +nazev_cviceni: Kvíz: retuš +nadpis_kvizu: Vyberte nástroj a vrstvu +--- + +# Výklad + +## Dva nástroje +*Razítko a štětec* + +- Klonovací razítko kopíruje konkrétní místo. Nejdřív Alt+klik do zdroje, pak malujete cíl. +- Opravný štětec bere texturu z okolí sám. Hodí se na pupínek, prach a malou skvrnu. +- Razítko berte tam, kde musí sedět vzor: plot, spára, textura zdi. +- Na velkou plochu oblohy je často rychlejší výběr a výplň než hodina razítka. + +> Tip: Alt+klik není obyčejný klik. Bez zdroje razítko neví, odkud brát. + +## Vrstva retus +*Nesahejte na fotku* + +- Nad fotku přidejte prázdnou vrstvu retus. +- Ve volbách razítka i štětce zapněte Vzorkovat všechny vrstvy, jinak prázdná vrstva nemá co kopírovat. +- Retuš pak jde ztlumit krytím nebo vypnout okem. Fotka vespod zůstane. +- Když se vzor opakuje jako razítko, vezměte zdroj z jiného místa. Jedna cihla pětkrát je vidět. + +> Tip: Tvrdost štětce kolem 50 % a krytí 100 %. Měkké razítko na hraně předmětu rozmaže obrys. + +## Co retušovat +*Míra* + +- Pryč patří prach, datum z fotoaparátu, koš v záběru a drát, který tam nemá být. +- Nepatří pryč pihy, jizva ani rys, který dělá člověka. To už není oprava, ale jiná tvář. +- Datum vypálené v rohu je pixel, ne vrstva. Musí se přemalovat z okolí oblohy nebo zdi. +- Po retuši se podívejte na 100 %. Opakovaný vzor a šmouha jsou vidět zblízka. + +> Tip: Jedna školní fotka, tři konkrétní chyby. Celou kůži nepřemalovávejte. + +## Zadání: tři chyby +*Cvičení* + +- Najděte na fotce tři rušivé věci: třeba koš, drát a datum, nebo tři skvrny. +- Odstraňte je na vrstvě retus. Razítko i štětec vzorkují všechny vrstvy. +- Vrstvu fotka nezamykejte malbou. Po vypnutí retus musí být chyby zpátky. +- Uložte retus.psd a do názvů si poznamenejte, který nástroj byl na které chybě. + +> Tip: Když po vypnutí vrstvy retus obraz vypadá stejně, malovali jste do fotky. Vezměte to z historie zpět. + +# Kvíz + +## Jak se u klonovacího razítka určí zdroj? +- Ctrl+J +- [x] Alt+klik do místa, které se má kopírovat +- Exportem +- Krytím 70 % +> Alt+klik vezme zdroj. Další tahy ho kopírují do cíle. + +## Kam se retuš maluje? +- Přímo do vrstvy fotky +- [x] Na prázdnou vrstvu nad fotkou +- Do textové vrstvy +- Do JPG +> Vlastní vrstva jde vypnout. Fotka zůstane původní. + +## Který nástroj si bere texturu z okolí sám? +- [x] Opravný štětec +- Text +- Ořez na 1920 px +- Pero +> Štětec dopočítá okolí. Razítko kopíruje přesně to místo, které jste označili. + +## Co musí být zapnuté, když razítko maluje do prázdné vrstvy? +- CMYK +- [x] Vzorkovat všechny vrstvy +- Spadávka 3 mm +- 25 sn/s +> Bez vzorkování všech vrstev je zdroj prázdný a razítko nemá co kopírovat. diff --git a/content/prezentacni-grafika/1-rocnik/07-text-a-barvy.en.md b/content/prezentacni-grafika/1-rocnik/07-text-a-barvy.en.md new file mode 100644 index 0000000..eb99105 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/07-text-a-barvy.en.md @@ -0,0 +1,6 @@ +--- +nazev: Type and colour +popis: A headline, the eyedropper and the foreground colour. +nazev_cviceni: Quiz: type and colour +nadpis_kvizu: Name the colour shortcuts +--- diff --git a/content/prezentacni-grafika/1-rocnik/07-text-a-barvy.md b/content/prezentacni-grafika/1-rocnik/07-text-a-barvy.md new file mode 100644 index 0000000..9abe13c --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/07-text-a-barvy.md @@ -0,0 +1,79 @@ +--- +id: pg-1-text-a-barvy +nazev: Text a barvy +popis: Nápis, kapátko a barva popředí. +nazev_cviceni: Kvíz: text a barvy +nadpis_kvizu: Poznejte zkratky barev +--- + +# Výklad + +## Barva popředí +*Kapátko* + +- Levý horní čtvereček je barva popředí, pravý dolní je pozadí. +- D vrátí černou a bílou. X je prohodí. +- Kapátko je I. Klik do fotky vezme barvu, která už v obraze je. +- Barvu nadpisu berte z fotky, ne náhodnou jedovatou zelenou, která se s ní pere. + +> Tip: Na světlé nebe patří tmavý text, na tmavý kabát světlý. Stejná šedá na obojí zanikne. + +## Nástroj text +*Písmo* + +- Jedno kliknutí založí bodový text. Tažení myší založí odstavcový rámeček. +- Nadpis školní práce je bodový text. Delší odstavec patří do rámečku. +- Velikost nadpisu na plátně 1920 px je kolem 72 pt. Podnadpis kolem 28 pt. +- Písmo nechte jedno, nejvýš dvě. Třetí řez plakát rozbije. + +> Tip: Textová vrstva se jmenuje podle nápisu. Nepřevádějte ji na pixely, dokud nemusíte. V PSD ať jde text přepsat. + +## Čitelnost +*Kontrast* + +- Když text sedí na rušné fotce, dejte pod něj poloprůhledný pruh, ne jen stín. +- Stín textu: vzdálenost malá, třeba 4 px, rozmazání 8 px, krytí kolem 40 %. +- Bílý text na světlé obloze nečetete ani vy. Ztmavte místo pod ním, nebo změňte barvu písma. +- Zarovnání na střed jen u krátkého nadpisu. Dlouhý text nechte vlevo. + +> Tip: Styl vrstvy je fx dole v panelu Vrstvy. Stín pak jde vypnout, písmo zůstane. + +## Zadání: nápis z fotky +*Cvičení* + +- Kapátkem I vezměte světlou barvu z fotky a nadpis Školní den napište barvou, která je k ní kontrastní. +- Podnadpis se jménem autora dejte na 28 pt. +- Pod text dejte pruh s krytím 70 %, ať nápis drží i na světlém místě. +- Uložte napis.psd. Text nechte jako textovou vrstvu, nerasterizujte ho. + +> Tip: X prohodí barvy, když kapátko vezme pravou. D vás vrátí na černou a bílou, když se ztratíte. + +# Kvíz + +## Co udělá klávesa D? +- Smaže vrstvu +- [x] Vrátí výchozí černou a bílou +- Spustí export +- Založí masku +> D je default. X potom černou a bílou prohodí. + +## Čím se nabere barva z fotky? +- Klonovacím razítkem +- [x] Kapátkem, klávesa I +- Ořezem +- Ctrl+0 +> Kapátko uloží barvu pixelu do popředí. Nadpis pak ladí s fotkou. + +## Jak velký má být podnadpis na plátně 1920 px? +- 4 pt +- [x] kolem 28 pt +- 300 pt +- 2480 pt +> Nadpis je velký, kolem 72 pt. Podnadpis je menší, kolem 28 pt, ať je vidět hierarchie. + +## Proč se text hned nerasterizuje? +- PSD by nešel uložit +- [x] V pracovním souboru má jít nápis přepsat +- Raster je vektor +- Premiere ho jinak nepřečte +> Z pixelů text už neopravíte. Na pixely ho převádějte až v kopii, když musíte. diff --git a/content/prezentacni-grafika/1-rocnik/08-zadani-fotografie.en.md b/content/prezentacni-grafika/1-rocnik/08-zadani-fotografie.en.md new file mode 100644 index 0000000..f4da788 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/08-zadani-fotografie.en.md @@ -0,0 +1,6 @@ +--- +nazev: Assignment: edit a photograph +popis: One photo, retouch, a selection, type and two exports. +nazev_cviceni: Quiz: the photograph +nadpis_kvizu: Check the finished piece +--- diff --git a/content/prezentacni-grafika/1-rocnik/08-zadani-fotografie.md b/content/prezentacni-grafika/1-rocnik/08-zadani-fotografie.md new file mode 100644 index 0000000..f330581 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/08-zadani-fotografie.md @@ -0,0 +1,79 @@ +--- +id: pg-1-zadani-fotografie +nazev: Zadání: úprava fotografie +popis: Jedna fotka, retuš, výběr, nápis a dva výstupy. +nazev_cviceni: Kvíz: úprava fotografie +nadpis_kvizu: Zkontrolujte hotovou práci +--- + +# Výklad + +## Co se odevzdává +*Rozsah* + +- Jedna fotka školní akce nebo místa, ořez na 1920 × 1080 px, RGB, 72 ppi. +- Pracovní soubor fotografie.psd a plochá kopie fotografie.jpg v kvalitě 10. +- Na plátně je nápis Školní den a pod ním vaše jméno. +- JPG nesmí být jediný soubor. Bez PSD nejde práce opravit. + +> Tip: Ořez udělá nástroj Ořez. Poměr stran zadejte 16 : 9, ať z fotky nezbude čtverec omylem. + +## Vrstvy, které musí být +*Skladba* + +- Dole fotka. Nad ní retus s aspoň jednou odstraněnou věcí, vzorkuje všechny vrstvy. +- Osoba nebo hlavní předmět je vybraný a leží na vlastní vrstvě, prolnutí okraje 2 px. +- Pozadí jde ztmavit samostatně. Po vypnutí této úpravy je pozadí zpět světlé. +- Skupina popisek drží pruh s krytím 70 % a oba texty. Texty nejsou rasterizované. + +> Tip: Pět vteřin kontroly: vypínejte skupiny okem. Každá musí něco změnit a po zapnutí to vrátit. + +## Nápis +*Čitelnost* + +- Barva nadpisu je z kapátka nebo k němu kontrastní. Na světlém místě nesmí zmizet. +- Podnadpis má asi 28 pt. Nadpis je výrazně větší. +- Pruh pod textem nelpí na horní hraně, sedí ve spodní třetině. +- Žádný text není oříznutý hranou plátna. Nechte od okraje aspoň 48 px. + +> Tip: 48 px je bezpečný okraj i na telefonu, když se JPG někde ořízne na krajích. + +## Kontrola před odevzdáním +*Seznam* + +- Obraz je 1920 × 1080 px. Větší export zmenšete, menší nezvětšujte rozmazáním. +- Retuš na 100 % nemá opakovaný vzor ani šmouhu přes obličej. +- V JPG nejsou vrstvy, v PSD jsou. Oba soubory se jmenují fotografie, liší se příponou. +- Původní fotku nemažte. Když ořez přetáhnete, musí být z čeho začít znovu. + +> Tip: Soubor > Exportovat jako ukáže rozměr. Když tam není 1920 × 1080, ořez ještě nesedí. + +# Kvíz + +## Jaký má být výsledný rozměr? +- 2480 × 3508 px +- [x] 1920 × 1080 px +- A4 při 300 ppi +- 80 × 80 px +> Zadání je obrazovka, Full HD, 16 : 9. A4 při 300 ppi by bylo do tisku. + +## Kde smí ležet retuš? +- Jen v JPG +- [x] Na vlastní vrstvě nad fotkou +- V rasterizovaném textu +- V dokumentu CMYK +> Vrstva retus jde vypnout. JPG je až plochá kopie na odevzdání. + +## Jaké krytí má pruh pod nápisem? +- 10 % +- [x] 70 % +- 300 % +- Pruh se nedělá +> 70 % podrží text a ještě nechá tušit fotku. Plný pruh ji zakryje celou. + +## Co se odevzdává? +- Jen JPG +- [x] PSD i JPG +- Jen SVG +- Projekt Premiere +> PSD je práce s vrstvami. JPG je náhled, který otevře i člověk bez Photoshopu. diff --git a/content/prezentacni-grafika/2-rocnik/01-masky.en.md b/content/prezentacni-grafika/2-rocnik/01-masky.en.md new file mode 100644 index 0000000..a1ed65d --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/01-masky.en.md @@ -0,0 +1,6 @@ +--- +nazev: Masks +popis: A mask hides, it does not delete. Black conceals, white reveals. +nazev_cviceni: Quiz: masks +nadpis_kvizu: Read the mask +--- diff --git a/content/prezentacni-grafika/2-rocnik/01-masky.md b/content/prezentacni-grafika/2-rocnik/01-masky.md new file mode 100644 index 0000000..8d02198 --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/01-masky.md @@ -0,0 +1,79 @@ +--- +id: pg-2-masky +nazev: Masky +popis: Maska skrývá, nemaže. Černá kryje, bílá ukazuje. +nazev_cviceni: Kvíz: masky +nadpis_kvizu: Přečtěte masku +--- + +# Výklad + +## K čemu maska je +*Nedestruktivně* + +- Smazaný pixel je pryč. Maska ho jen schová a jde přemalovat zpátky. +- Každá vrstva může mít jednu masku. Bílá plocha masky obraz ukazuje, černá ho kryje. +- Šedá je poloprůhledná. 50 % šedá nechá vrstvu napůl vidět. +- Masku přidáte ikonou obdélníku s kolečkem dole v panelu Vrstvy. + +> Tip: Když mažete gumou, nemáte cestu zpět. Když malujete do masky černou, stačí přetřít bílou. + +## Malování do masky +*Štětec* + +- Klik na miniaturu masky, ne na miniaturu obrazu. Jinak malujete do fotky. +- Alt+klik na masku ji ukáže přes celé plátno. Černobílý obraz je jen maska, ne výsledek. +- Měkký štětec, tvrdost 0 %, krytí 30 %, černá barva. Okraj předmětu pak není vystřižený nůžkami. +- X prohodí černou a bílou, takže stejný štětec umí skrýt i vrátit. + +> Tip: Kolem miniatury masky je rámeček, když je aktivní. Rámeček kolem fotky znamená, že malujete špatně. + +## Výběr a maska +*Spolu* + +- Výběr postavy a pak ikona masky schová všechno mimo výběr. +- Prolnutí 2 px udělejte ještě ve výběru, nebo okraj domalujte měkkým štětcem. +- Maska není nová vrstva. V panelu sedí vpravo vedle miniatury. +- Masku neslučujte s vrstvou, dokud učitel neřekne. Sloučení už nejde odmaskovat. + +> Tip: Ctrl+I na masce, když je vybraná, černou a bílou otočí. Hodí se, když jste skryli opak. + +## Zadání: výřez maskou +*Cvičení* + +- Postavu oddělte maskou, ne gumou. Původní fotka po Alt+klik a přemalování bílou musí být celá. +- Okraj dopracujte štětcem tvrdost 0 % a krytí 30 %. +- Pozadí nechte ve vrstvě pod ní, ať jde vyměnit. +- Uložte maska.psd. V souboru nesmí být jen jedna sloučená vrstva. + +> Tip: Vypněte a zapněte masku Shiftem a klikem na ni. Obraz se musí vrátit celý. + +# Kvíz + +## Co dělá černá barva v masce? +- Vrstvu ukáže +- [x] Vrstvu skryje +- Převede ji na text +- Zvýší rozlišení na 300 ppi +> Černá kryje, bílá ukazuje, šedá je mezi tím. + +## Čím se maska zobrazí přes celé plátno? +- Ctrl+1 +- [x] Alt+klik na miniaturu masky +- Exportem JPG +- Klávesou D na textu +> Alt+klik ukáže černobílou masku. Další Alt+klik vrátí normální obraz. + +## Proč se výřez nedělá gumou? +- Guma je v Illustratoru +- [x] Smazané pixely už nevrátíte, masku ano +- Guma umí jen CMYK +- Premiere gumu nepustí +> Masku přemalujete. Guma na obyčejné vrstvě pixely zahodí. + +## Jaké krytí štětce se hodí na jemný okraj masky? +- 100 % tvrdý štětec +- [x] 30 % a tvrdost 0 % +- Krytí textu 28 pt +- 10 Mb/s +> Nízké krytí a měkký štětec skládají hranu postupně. Tvrdý tah je vidět jako střih. diff --git a/content/prezentacni-grafika/2-rocnik/02-urovne-a-krivky.en.md b/content/prezentacni-grafika/2-rocnik/02-urovne-a-krivky.en.md new file mode 100644 index 0000000..0ae3082 --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/02-urovne-a-krivky.en.md @@ -0,0 +1,6 @@ +--- +nazev: Levels and curves +popis: Brightness and contrast as an adjustment layer, not a rewrite of the pixels. +nazev_cviceni: Quiz: curves +nadpis_kvizu: Recognise a non-destructive edit +--- diff --git a/content/prezentacni-grafika/2-rocnik/02-urovne-a-krivky.md b/content/prezentacni-grafika/2-rocnik/02-urovne-a-krivky.md new file mode 100644 index 0000000..cc53a24 --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/02-urovne-a-krivky.md @@ -0,0 +1,79 @@ +--- +id: pg-2-krivky +nazev: Úrovně a křivky +popis: Světlost a kontrast jako vrstva úprav, ne jako přepis pixelů. +nazev_cviceni: Kvíz: křivky +nadpis_kvizu: Poznejte nedestruktivní úpravu +--- + +# Výklad + +## Vrstva úprav +*Nad pixely* + +- Vrstva úprav mění vzhled všeho pod sebou a pixely fotky nepřepisuje. +- Přidá se kolečkem dole v panelu Vrstvy. Položka Křivky nebo Úrovně. +- Když úpravu vypnete okem, fotka je jako na začátku. +- Úprava přímo přes Obraz > Přizpůsobení fotku přepíše. Ve školní práci ji neberte. + +> Tip: Vrstva úprav má vlastní masku. Černou v masce úpravu na části obrazu vypnete. + +## Úrovně +*Tři body* + +- Černý bod říká, co má být nejtmavší. Bílý bod, co má být nejsvětlejší. +- Šedý bod srovná nádech. Klik do místa, které má být neutrální šedé. +- Bod světel nechte kolem 245, ne na 255, ať v obloze nezmizí kresba. +- Bod stínů kolem 15, ne na 0, ať černá není slitá. + +> Tip: Když histogram na obou koncích nic nemá, fotka je mdlá. Body přitáhněte k místu, kde data začínají. + +## Křivka S +*Kontrast* + +- Křivka zdola zleva nahoru doprava je světlost od stínů ke světlům. +- Jemná křivka do S zvedne kontrast: stíny o trochu níž, světla o trochu výš. +- Ostré S přepálí tváře a zabije stíny. Stačí malý oblouk. +- Jedna vrstva Křivky na celou fotku. Druhou přidejte jen na oblohu, a maskou ji tam omezte. + +> Tip: Na křivce táhněte málo. Oko si na přepálený náhled rychle zvykne a původní fotka pak vypadá špatně, i když byla lepší. + +## Zadání: dvě úpravy +*Cvičení* + +- Nad fotku dejte vrstvu úprav Křivky. Bod stínů nechte u 15, bod světel u 245, mezi tím jemné S. +- Druhou křivku omezte maskou jen na pozadí, ať tvář nejde do přepalu. +- Obě úpravy pojmenujte. Fotka vespod ať nemá žádné Přizpůsobení zapékané v pixelech. +- Uložte krivky.psd. Vypnutí obou úprav vrátí původní světlost. + +> Tip: Alt při tahu černého a bílého bodu v Úrovních ukáže, které místo už je slité. U křivek stačí oko a 100 %. + +# Kvíz + +## Kam patří školní zesvětlení fotky? +- Obraz > Přizpůsobení, přímo do pixelů +- [x] Vrstva úprav nad fotkou +- Do textové vrstvy +- Do exportu JPG jako jediná úprava +> Vrstva úprav jde vypnout. Přizpůsobení přes nabídku Obraz pixely přepíše. + +## Kam dát bod světel, aby obloha neztratila kresbu? +- 0 +- [x] kolem 245 +- 255 a přes +- 28 pt +> 255 je čistá bílá bez kresby. 245 nechá ve světlech detail. + +## Co dělá jemná křivka ve tvaru S? +- Sníží rozlišení +- [x] Zvedne kontrast +- Převede RGB na vektor +- Přidá spadávku +> Stíny jdou trochu níž a světla trochu výš. Kontrast stoupne, barva se nemění v jiný režim. + +## Čím se křivka omezí jen na pozadí? +- Gumou na fotce +- [x] Maskou vrstvy úprav +- Exportem +- Krytím textu +> Černá v masce úpravy řekne: tady křivku nepoužívej. Tvář může zůstat bílá, tedy upravená jinou vrstvou, nebo naopak. diff --git a/content/prezentacni-grafika/2-rocnik/03-chytre-objekty-a-filtry.en.md b/content/prezentacni-grafika/2-rocnik/03-chytre-objekty-a-filtry.en.md new file mode 100644 index 0000000..5811b67 --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/03-chytre-objekty-a-filtry.en.md @@ -0,0 +1,6 @@ +--- +nazev: Smart objects and filters +popis: Blur and sharpening you can still change. +nazev_cviceni: Quiz: filters +nadpis_kvizu: Fill in the filter values +--- diff --git a/content/prezentacni-grafika/2-rocnik/03-chytre-objekty-a-filtry.md b/content/prezentacni-grafika/2-rocnik/03-chytre-objekty-a-filtry.md new file mode 100644 index 0000000..78c29d2 --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/03-chytre-objekty-a-filtry.md @@ -0,0 +1,79 @@ +--- +id: pg-2-chytre-objekty +nazev: Chytré objekty a filtry +popis: Rozostření a doostření, které jde změnit. +nazev_cviceni: Kvíz: filtry +nadpis_kvizu: Doplňte hodnoty filtru +--- + +# Výklad + +## Chytrý objekt +*Obal* + +- Vrstva > Chytré objekty > Převést na chytrý objekt zabalí vrstvu do obalu. +- Filtr na chytrém objektu zůstane jako položka pod vrstvou. Dvojklik ho otevře znovu. +- Filtr na obyčejné vrstvě pixely přepíše. Ve školním plakátu ho neberte. +- Logo z Illustratoru vkládejte rovnou jako chytrý objekt, ať jde zvětšit. + +> Tip: V rohu miniatury chytrého objektu je malá ikona. Když tam není, filtr se zapíše natvrdo. + +## Rozostření pozadí +*Hloubka* + +- Gaussovské rozostření oddělí pozadí od předmětu. Na pozadí stačí 8 px. +- Předmět rozostřením nechte být. Buď je na vrstvě nad ním, nebo má masku. +- Silnější rozostření, třeba 30 px, už pozadí smaže. Berte ho jen jako záměr. +- Rozostření celého obrazu včetně tváře je chyba, ne hloubka ostrosti. + +> Tip: Nejdřív výřez maskou z minulé lekce, potom rozostření jen vrstvy pozadí. + +## Doostření +*Unsharp* + +- Filtr Doostření (Unsharp Mask) zvedne kontrast na hraně. Není to zvětšení pixelů. +- Školní hodnoty na fotku: množství 80 %, poloměr 1 px, práh 2. +- Vyšší množství dělá světlý a tmavý obrys kolem větví a vlasů. +- Doostřujte až nakonec a jen jednou. Doostřené doostření je vidět. + +> Tip: Náhled filtru přepínejte, ať vidíte rozdíl. Na 100 %, ne na zmenšeném plátně. + +## Zadání: hloubka a hrana +*Cvičení* + +- Pozadí převeďte na chytrý objekt a dejte mu Gaussovské rozostření 8 px. +- Předmět nechte ostrý. Maska nebo samostatná vrstva ho chrání. +- Na předmět dejte Doostření 80 %, poloměr 1 px, práh 2, taky jako filtr chytrého objektu. +- Uložte filtry.psd. Oba filtry musí jít otevřít dvojklikem a hodnoty změnit. + +> Tip: Když dvojklik filtr neotevře, vrstva nebyla chytrý objekt. Vezměte krok z historie a převeďte ji znovu. + +# Kvíz + +## Jaké rozostření má školní pozadí? +- [x] 8 px Gaussovské +- 80 px na celou fotku včetně tváře +- 0 px +- Rozostření se dělá v Premiere +> 8 px pozadí oddělí a ještě ho nechá poznat. Celý obraz včetně předmětu se nerozostřuje. + +## Jaké hodnoty má školní Doostření? +- [x] množství 80 %, poloměr 1 px, práh 2 +- množství 300 %, poloměr 20 px +- jen krytí 70 % +- 300 ppi +> Malý poloměr drží hranu. Velký poloměr kreslí kolem předmětů svatozář. + +## Proč filtr patří na chytrý objekt? +- Jinak nejde uložit PSD +- [x] Hodnoty jdou potom změnit a pixely se nepřepíšou +- Chytrý objekt je vždy CMYK +- Kvůli 25 sn/s +> Pod vrstvou zůstane řádek filtru. Dvojklik ho otevře se stejnými čísly. + +## Co Doostření ve skutečnosti dělá? +- Dopočítá nové pixely jako při zvětšení +- [x] Zvedne kontrast na hraně +- Převede fotku na křivky +- Přidá masku +> Unsharp Mask není zvětšení. Měkkou fotku z malého souboru nezachrání, jen zdůrazní hranu. diff --git a/content/prezentacni-grafika/2-rocnik/04-tvary-a-styly.en.md b/content/prezentacni-grafika/2-rocnik/04-tvary-a-styly.en.md new file mode 100644 index 0000000..82e7d6e --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/04-tvary-a-styly.en.md @@ -0,0 +1,6 @@ +--- +nazev: Shapes and layer styles +popis: A vector bar, a shadow and a stroke inside Photoshop. +nazev_cviceni: Quiz: shapes +nadpis_kvizu: Fill in the bar and the shadow +--- diff --git a/content/prezentacni-grafika/2-rocnik/04-tvary-a-styly.md b/content/prezentacni-grafika/2-rocnik/04-tvary-a-styly.md new file mode 100644 index 0000000..fba437e --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/04-tvary-a-styly.md @@ -0,0 +1,79 @@ +--- +id: pg-2-tvary +nazev: Tvary a styly vrstvy +popis: Vektorový pruh, stín a tah přímo ve Photoshopu. +nazev_cviceni: Kvíz: tvary +nadpis_kvizu: Doplňte rozměr pruhu a stínu +--- + +# Výklad + +## Tvar +*Vektor ve Photoshopu* + +- Nástroj obdélník a elipsa kreslí tvarovou vrstvu. Není to namalovaný pixel. +- Tvar jde zvětšit a hrana zůstane hladká, podobně jako v Illustratoru. +- Barvu změníte dvojklikem na miniaturu. Nemalujte přes tvar štětcem. +- Na fotku 1920 × 1080 patří spodní pruh 1920 × 280 px, od kraje ke kraji. + +> Tip: Když podržíte Shift, elipsa je kružnice a obdélník čtverec. + +## Styl vrstvy +*fx* + +- fx dole v panelu přidá stín, tah nebo přechod. Písmo ani tvar se nerasterizuje. +- Stín školního pruhu: vzdálenost 8 px, velikost 16 px, krytí 40 %. +- Tah 2 px berte jen na tlačítko nebo rámeček. Na velký pruh je zbytečný. +- Styl zkopírujete Alt+tažením řádku fx na jinou vrstvu. + +> Tip: Krytí ve stínu není krytí celé vrstvy. Pruh může být plný a stín lehký. + +## Pruh a text +*Spolu* + +- Pruh je jedna vrstva, text druhá. Neslučujte je. +- Pruh má krytí 75 %, ať fotka pod ním nezmizí celá. +- Text je bílý, když je pruh tmavý. Sedí aspoň 48 px od levého okraje pruhu. +- Skupina spodni drží pruh i text. Vypnutí skupiny je sundá najednou. + +> Tip: Zarovnání textu k pruhu: vyberte obě vrstvy a v nástroji přesun použijte ikony zarovnání. + +## Zadání: spodní pruh +*Cvičení* + +- Na plátně 1920 × 1080 nakreslete tvar 1920 × 280 px, krytí vrstvy 75 %. +- Stín: vzdálenost 8, velikost 16, krytí stínu 40 %. +- Bílý nadpis a podnadpis 28 pt jsou ve skupině s pruhem, 48 px od levého kraje. +- Uložte pruh.psd. Tvar nesmí být rasterizovaný. + +> Tip: V panelu Vrstvy je u tvaru ikona, ne jen malba. Když chybí, kreslili jste štětcem. + +# Kvíz + +## Jak velký je spodní pruh na Full HD? +- 280 × 280 px +- [x] 1920 × 280 px +- 2480 × 3508 px +- A4 +> Šířka plátna je 1920. Výška pruhu 280 nechá fotce horní část. + +## Jaké krytí má vrstva pruhu? +- [x] 75 % +- 10 % +- 100 % a navíc CMYK +- Krytí se nenastavuje +> 75 % pruh podrží text a fotku pod ním ještě tušíte. + +## Jaký stín má školní pruh? +- [x] vzdálenost 8, velikost 16, krytí 40 % +- vzdálenost 80, velikost 80, krytí 100 % +- žádný, stín nejde u tvaru +- jen filtr 8 px na fotku +> Malý stín pruh odlepí od fotky. Velký stín přes celou fotku je špína, ne styl. + +## Proč se tvar nespojuje s textem do jedné vrstvy? +- Photoshop to neumí oddělit +- [x] Text by nešel přepsat a pruh přebarvit zvlášť +- Kvůli 25 sn/s +- JPG umí vrstvy líp +> Každá věc vlastní vrstvu. Sloučení nechte až na plochý export. diff --git a/content/prezentacni-grafika/2-rocnik/05-kompozice-plakatu.en.md b/content/prezentacni-grafika/2-rocnik/05-kompozice-plakatu.en.md new file mode 100644 index 0000000..41ced0b --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/05-kompozice-plakatu.en.md @@ -0,0 +1,6 @@ +--- +nazev: Poster composition +popis: A3, a 3 mm bleed and one main headline. +nazev_cviceni: Quiz: the poster +nadpis_kvizu: Pick the size and the bleed +--- diff --git a/content/prezentacni-grafika/2-rocnik/05-kompozice-plakatu.md b/content/prezentacni-grafika/2-rocnik/05-kompozice-plakatu.md new file mode 100644 index 0000000..a52cbb3 --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/05-kompozice-plakatu.md @@ -0,0 +1,79 @@ +--- +id: pg-2-kompozice +nazev: Kompozice plakátu +popis: A3, spadávka 3 mm a jeden hlavní nápis. +nazev_cviceni: Kvíz: plakát +nadpis_kvizu: Vyberte formát a spadávku +--- + +# Výklad + +## Formát a spadávka +*A3* + +- Školní plakát je A3 na výšku, 297 × 420 mm, 300 ppi, RGB. +- Tiskárna ořízne papír na čistý A3. Barva, která má jít do kraje, musí přesahovat. +- Spadávka je 3 mm na každé straně. Plátno tedy má 303 × 426 mm. +- Vodítka dejte 3 mm od okraje. Dovnitř vodítek patří text, ven až k okraji plátna smí jen pozadí. + +> Tip: 303 mm je 297 plus 3 a 3. Když je plátno jen 297, spadávka tam není. + +## Jeden plakát, jedna věc +*Hierarchie* + +- Největší je název akce. Pod ním datum a místo, menším písmem. +- Datum školního zadání je 12. 6. Místo je tělocvična. +- Hlavní prvek, třeba postava, sedí na třetině, ne uprostřed jako pasové foto. +- Dva stejně velké nadpisy se perou. Jeden musí vyhrát. + +> Tip: Třetiny: dvě svislé a dvě vodorovné čáry dělí plochu na devět polí. Průsečík je silné místo. + +## Okraj textu +*Klid* + +- Text je aspoň 10 mm od čistého ořezu, tedy dál než vodítko spadávky. +- Řádek delší než dvě třetiny šířky se čte špatně. Nadpis zalomte. +- Písmo nejvýš dvě. Jedno tučné na název, jedno obyčejné na údaj. +- Logo školy je malé a v rohu. Není to druhý plakát. + +> Tip: Když logo nemáte ve vektoru, berte největší PNG, které škola má, a zmenšete ho. Ne naopak. + +## Zadání: kostra plakátu +*Cvičení* + +- Nový dokument 303 × 426 mm, 300 ppi, RGB. Vodítka 3 mm od každého kraje. +- Fotku dejte přes celou spadávku. Pruh a text až za vodítka. +- Název akce, pod ním 12. 6. a tělocvična. Název je výrazně větší. +- Uložte plakat-kostra.psd. Čistý formát A3 ať jde poznat podle vodítek. + +> Tip: Zobrazení > Pravítka a táhněte vodítko z pravítka. Přichytávání k němu zapněte v menu Zobrazení. + +# Kvíz + +## Jak velké je plátno plakátu se spadávkou 3 mm? +- 297 × 420 mm +- [x] 303 × 426 mm +- 1920 × 1080 px +- 210 × 297 mm +> A3 je 297 × 420. Z každé strany 3 mm navíc dělá 303 × 426. + +## Kam nesmí text? +- [x] Do spadávky a blízko ořezu +- Za vodítko, dovnitř čistého formátu +- Pod název +- Na vlastní vrstvu +> Ořez spadávku uřízne. Text 10 mm od čistého kraje přežije i křivý řez. + +## Kam se má strefit hlavní prvek? +- Přesně na střed jako na občanku +- [x] Na třetinu plochy +- Do razítka +- Mimo plátno +> Střed je klidný a často nudný. Průsečík třetin drží oko. + +## Jaké rozlišení má tiskový plakát? +- 72 ppi +- [x] 300 ppi +- 25 sn/s +- 8 px +> Plakát je tisk. 300 ppi. 72 ppi by na A3 bylo vidět jako mřížka. diff --git a/content/prezentacni-grafika/2-rocnik/06-mockup.en.md b/content/prezentacni-grafika/2-rocnik/06-mockup.en.md new file mode 100644 index 0000000..538410f --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/06-mockup.en.md @@ -0,0 +1,6 @@ +--- +nazev: A mockup +popis: The poster design placed on a photograph of a wall. +nazev_cviceni: Quiz: the mockup +nadpis_kvizu: Pick the blend mode and the opacity +--- diff --git a/content/prezentacni-grafika/2-rocnik/06-mockup.md b/content/prezentacni-grafika/2-rocnik/06-mockup.md new file mode 100644 index 0000000..bc41379 --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/06-mockup.md @@ -0,0 +1,79 @@ +--- +id: pg-2-mockup +nazev: Mockup +popis: Návrh plakátu usazený na fotografii zdi. +nazev_cviceni: Kvíz: mockup +nadpis_kvizu: Vyberte režim a krytí +--- + +# Výklad + +## K čemu mockup je +*Náhled* + +- Mockup ukáže návrh v situaci: plakát na zdi, vizitka na stole, tričko na postavě. +- Není to náhrada tiskového PDF. Je to obrázek pro prezentaci práce. +- Fotka zdi má být velká aspoň jako výstup, ideálně 1920 px na delší straně. +- Návrh plakátu vložte jako chytrý objekt, ať ho jde ještě opravit. + +> Tip: Obraz > Umístit vložené, nebo přetáhněte PSD a potvrďte. Vznikne chytrý objekt. + +## Perspektiva +*Deformace* + +- Úpravy > Transformovat > Pokřivit, nebo Deformace, srovná rohy plakátu s rohy na zdi. +- Rohy táhněte na fotografii, ne od oka do prázdna. +- Shift při tažení rohu není vždy správně. Zeď má úběžník, plakát na ní není obdélník v pohledu. +- Po potvrzení transformace chytrý objekt deformaci podrží. + +> Tip: Když je zeď focená z boku, horní hrana plakátu je kratší než dolní, nebo naopak. To má mockup ukázat. + +## Potisk +*Násobit* + +- Režim prolnutí Násobit (Multiply) nechá prosvítit strukturu zdi. +- Krytí návrhu dejte 90 %. 100 % vypadá jako nalepená samolepka bez papíru. +- Bílá v režimu Násobit zmizí. Na bílý plakát se Násobit nehodí, nechte Normální a lehce snižte krytí. +- Stín pod plakátem: měkký, malý, na vlastní vrstvě, ať odlepí papír od zdi. + +> Tip: Násobit násobí barvy. Tmavé místa zůstanou tmavá, bílá se ztratí. + +## Zadání: plakát na zdi +*Cvičení* + +- Vezměte kostru plakátu a usaďte ji na fotku zdi jako chytrý objekt. +- Deformací srovnejte čtyři rohy. Krytí 90 %. +- Když plakát není bílý, zkuste Násobit. Když zbělá, vraťte Normální. +- Uložte mockup.psd na 1920 px delší strany, RGB. Tiskové PDF z toho nedělejte. + +> Tip: Mockup je obrazovka, 72 ppi stačí. Tiskové rozměry zůstávají v plakat-kostra.psd. + +# Kvíz + +## K čemu je mockup? +- Je to tiskové PDF pro tiskárnu +- [x] Ukáže návrh na fotografii místa +- Nahradí vrstvy v PSD +- Je to střih videa +> Tiskárna dostane PDF v ořezu. Mockup je na obhajobu a na web. + +## Jak se návrh srovná se zdí, která je focená z boku? +- Ořezem na čtverec +- [x] Deformací rohů +- Filtrem Doostření +- Převodem do CMYK +> Pokřivení nebo deformace vezme každý roh zvlášť. Obyčejné zmenšení úhel zdi neopraví. + +## Kdy dává smysl režim Násobit? +- [x] Když má prosvítit struktura zdi a plakát není bílý +- Vždy, i na bílý plakát +- Jen u textu 28 pt +- Jen v Premiere +> Násobit nechá tmavou strukturu a bílou zruší. Bílý plakát by zmizel. + +## Jaké krytí má návrh na zdi? +- [x] 90 % +- 10 % +- 100 % bez výjimky +- 300 % +> 90 % lehce spojí papír se zdí. 100 % často vypadá jako nalepený obdélník. diff --git a/content/prezentacni-grafika/2-rocnik/07-export-web-a-tisk.en.md b/content/prezentacni-grafika/2-rocnik/07-export-web-a-tisk.en.md new file mode 100644 index 0000000..1619e68 --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/07-export-web-a-tisk.en.md @@ -0,0 +1,6 @@ +--- +nazev: Export for screen and print +popis: JPG and PNG for the screen, a PDF with crop marks for the printer. +nazev_cviceni: Quiz: export +nadpis_kvizu: Match the format to the job +--- diff --git a/content/prezentacni-grafika/2-rocnik/07-export-web-a-tisk.md b/content/prezentacni-grafika/2-rocnik/07-export-web-a-tisk.md new file mode 100644 index 0000000..593d41c --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/07-export-web-a-tisk.md @@ -0,0 +1,79 @@ +--- +id: pg-2-export +nazev: Export pro web a tisk +popis: JPG a PNG na obrazovku, PDF s ořezem do tiskárny. +nazev_cviceni: Kvíz: export +nadpis_kvizu: Přiřaďte formát k použití +--- + +# Výklad + +## Tři výstupy +*Formát* + +- JPG je fotka bez průhlednosti. Kvalita 10, tedy kolem 80 %, na web. +- PNG drží průhlednost. Berte ho na logo a ikonu, ne na velkou fotku. +- PDF je pro tiskárnu. Vrstvy v něm být nemusí, musí sedět rozměr a ořez. +- PSD není výstup. PSD je pracovní soubor a odevzdává se k němu. + +> Tip: GIF na fotku neberte. Umí málo barev a školní plakát v něm pruhuje. + +## Web +*sRGB* + +- Delší strana webového JPG je 1600 px. Větší soubor na stránku školy netřeba. +- Barvy převeďte do sRGB, ať je telefon i projektor čtou stejně. +- Do webového JPG nedávejte spadávku ani ořezové značky. Ty jsou pro tiskárnu. +- Náhled pojmenujte plakat-web.jpg. Jméno Bez názvu-1.jpg se ve složce ztratí. + +> Tip: Úpravy > Převést do profilu, cíl sRGB. Obrazovkový JPG pak není v tajném profilu tiskárny. + +## Tisk +*Značky* + +- PDF plakátu je v rozměru se spadávkou, 303 × 426 mm, 300 ppi. +- Ořezové značky ukážou, kudy vede čistý A3. Ve Photoshopu je přidáte při exportu PDF, nebo je nakreslíte do spadávky, ne do textu. +- Do PDF nepatří vodítka a názvy vrstev. Patří do něj plochý obraz. +- Písmo, které tiskárna nemá, do PDF vloží export. V Photoshopu je text stejně často už vykreslený v obraze, to tiskárně nevadí. + +> Tip: Soubor > Uložit kopii a formát Photoshop PDF. Zaškrtněte, že se nemá ptát na vrstvy, ať je PDF ploché. + +## Zadání: dva výstupy z jednoho PSD +*Cvičení* + +- Z plakátu udělejte plakat-web.jpg, delší strana 1600 px, sRGB, kvalita 10, bez značek. +- Z téhož PSD udělejte plakat-tisk.pdf, 303 × 426 mm, 300 ppi, s ořezovými značkami. +- PSD plakat.psd si nechte s vrstvami. Do PDF vrstvy nedávejte. +- Obě kopie otevřete a zkontrolujte: JPG nemá značky, PDF nemá oříznutý text. + +> Tip: Když je JPG větší než 1600 px, obraz jste zmenšili špatně. Obraz > Velikost obrazu, přepočet Automaticky. + +# Kvíz + +## Který formát drží průhledné logo? +- JPG +- [x] PNG +- MP4 +- WAV +> JPG průhlednost vyplní bílou nebo černou. PNG ji umí. + +## Jak široký je webový náhled plakátu? +- [x] 1600 px na delší straně +- 303 mm +- 80 px +- Full HD a zároveň 300 ppi +> 1600 px stačí na obrazovku. Tiskové milimetry do JPG nepatří. + +## Co musí mít tiskové PDF plakátu? +- [x] Rozměr se spadávkou a ořezové značky +- Vrstvy Photoshopu a vodítka +- 25 sn/s +- Průhledné pozadí jako PNG +> Tiskárna řeže podle značek. Vrstvy a vodítka jsou věc PSD, ne PDF. + +## Který soubor je pracovní a nejde nahradit exportem? +- JPG +- [x] PSD +- PDF +- PNG +> Z PSD uděláte JPG i PDF znovu. Z JPG vrstvy neobnovíte. diff --git a/content/prezentacni-grafika/2-rocnik/08-zadani-plakat.en.md b/content/prezentacni-grafika/2-rocnik/08-zadani-plakat.en.md new file mode 100644 index 0000000..6f7f064 --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/08-zadani-plakat.en.md @@ -0,0 +1,6 @@ +--- +nazev: Assignment: a poster +popis: A3 with bleed, a mask, curves and two exports. +nazev_cviceni: Quiz: the finished poster +nadpis_kvizu: Check the print file and the web file +--- diff --git a/content/prezentacni-grafika/2-rocnik/08-zadani-plakat.md b/content/prezentacni-grafika/2-rocnik/08-zadani-plakat.md new file mode 100644 index 0000000..994b6d0 --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/08-zadani-plakat.md @@ -0,0 +1,79 @@ +--- +id: pg-2-zadani-plakat +nazev: Zadání: plakát +popis: A3 se spadávkou, maskou, křivkami a dvěma exporty. +nazev_cviceni: Kvíz: hotový plakát +nadpis_kvizu: Zkontrolujte tisk i web +--- + +# Výklad + +## Zadání akce +*Obsah* + +- Plakát zve na školní den 12. 6. v tělocvičně. Název si zvolte jeden a držte ho. +- Formát A3 na výšku, plátno 303 × 426 mm, 300 ppi, RGB, spadávka 3 mm. +- Na plakátu je fotka, jeden hlavní nápis, datum, místo a malé logo nebo značka školy. +- Text je aspoň 10 mm od čistého ořezu. Ve spadávce smí být jen pozadí. + +> Tip: Jeden plakát, jedna akce. Nepřidávejte druhé datum a druhý sál. + +## Jak je složený +*Vrstvy* + +- Skupina foto: fotka, maska předmětu, křivky jako vrstva úprav, pozadí rozostřené 8 px. +- Skupina grafika: tvarový pruh 75 % a případný další tvar. Stín pruhu 8 / 16 / 40 %. +- Skupina text: název a údaje, pořád jako text, ne jako pixely. +- Vypnutí kterékoli skupiny musí být vidět. Když ne, je skupina prázdná nebo je všechno sloučené. + +> Tip: Křivka má bod světel kolem 245 a stínů kolem 15. Doostření předmětu 80 %, 1 px, práh 2. + +## Dva výstupy +*Soubory* + +- plakat.psd s vrstvami. +- plakat-web.jpg, delší strana 1600 px, sRGB, kvalita 10, bez ořezových značek. +- plakat-tisk.pdf, 303 × 426 mm, ořezové značky, bez vrstev. +- Mockup na zdi je navíc, ne místo PDF. Když ho přidáte, je to mockup.psd. + +> Tip: Soubory leží v jedné složce plakat. Název Bez názvu v odevzdání neberte. + +## Kontrola +*Než to odevzdáte* + +- Na 100 % nemá retuš opakovaný vzor a maska nemá barevný lem. +- Datum 12. 6. je celé a čitelné. Logo není rozmazaný JPG zvětšený z 80 px. +- Webový JPG otevřený na celou obrazovku nemá značky ani šedé okraje plátna. +- PDF má stejný název akce jako PSD. Dvě verze textu jsou chyba. + +> Tip: Vytiskněte si PDF na A4 jako náhled. Když je text u kraje už na A4, na A3 bude u řezu taky. + +# Kvíz + +## Jaké je plátno tiskového plakátu? +- 1920 × 1080 px, 72 ppi +- [x] 303 × 426 mm, 300 ppi +- A4 bez spadávky +- 1600 px +> A3 je 297 × 420, spadávka 3 mm z každé strany přidá 6 mm. 300 ppi je tisk. + +## Jak je rozostřené pozadí? +- [x] 8 px, předmět zůstane ostrý +- 8 px přes celý plakát včetně textu +- 80 % Doostření na pozadí +- Nijak +> Rozostření oddělí pozadí. Text a hlavní předmět jsou ostré. + +## Co je ve webovém JPG navíc chyba? +- sRGB a 1600 px +- [x] Ořezové značky +- Kvalita kolem 10 +- Název akce +> Značky jsou pro tiskárnu. Na webu vypadají jako čáry v rozích. + +## Který soubor musí obsahovat vrstvy? +- plakat-web.jpg +- [x] plakat.psd +- plakat-tisk.pdf +- Žádný +> PSD je práce. JPG a PDF jsou ploché kopie. diff --git a/content/prezentacni-grafika/3-rocnik/01-projekt-a-media.en.md b/content/prezentacni-grafika/3-rocnik/01-projekt-a-media.en.md new file mode 100644 index 0000000..2474ba9 --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/01-projekt-a-media.en.md @@ -0,0 +1,6 @@ +--- +nazev: Project and media +popis: A Full HD sequence, 25 fps and bins for the media. +nazev_cviceni: Quiz: the project +nadpis_kvizu: Set up the right sequence +--- diff --git a/content/prezentacni-grafika/3-rocnik/01-projekt-a-media.md b/content/prezentacni-grafika/3-rocnik/01-projekt-a-media.md new file mode 100644 index 0000000..791315e --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/01-projekt-a-media.md @@ -0,0 +1,79 @@ +--- +id: pg-3-projekt +nazev: Projekt a média +popis: Sekvence Full HD, 25 sn/s a složky materiálu. +nazev_cviceni: Kvíz: projekt +nadpis_kvizu: Založte správnou sekvenci +--- + +# Výklad + +## Projekt +*Soubor* + +- Premiere ukládá práci do projektu s příponou prproj. Video v něm není, jsou v něm odkazy. +- Materiál a projekt mějte v jedné složce. Když video přesunete, Premiere ho nenajde. +- Ztracený soubor se připojí znovu: pravý klik, Propojit médium, a ukážete novou cestu. +- Automatické ukládání nechte zapnuté. Projekt se jinak po pádu nevrátí celý. + +> Tip: prproj není mp4. Otevře ho jen Premiere. Na odevzdání videa je až export. + +## Sekvence +*25 sn/s* + +- Sekvence je časová osa s daným rozměrem a snímkovou frekvencí. +- Školní zadání: 1920 × 1080, čtvercové pixely, 25 snímků za sekundu. +- 25 sn/s je běžné evropské video. Materiál z telefonu má často 30. Sekvenci přesto založte na 25, Premiere snímky přepočítá. +- Pojmenujte ji skolni-den. Název Sekvence 01 se ve větším projektu ztratí. + +> Tip: Nová sekvence je v panelu Projekt ikonou nového objektu. Předvolba DSLR nebo Digital SLR 1080p25 se trefí. + +## Složky +*Pořádek* + +- V panelu Projekt založte složky Záběry, Zvuk a Grafika. +- Import je Ctrl+I, nebo soubor přetáhnete do správné složky. +- Do Grafiky patří logo a titulky. Do Zvuku hudba a komentář. Do Záběrů jen video. +- Na časovou osu netáhněte deset záběrů najednou, dokud ve složce nemají pořádek. + +> Tip: Přejmenujte záběry podle místa, ne podle DSC_1842. Za rok nepoznáte, co to bylo. + +## Zadání: prázdný projekt +*Cvičení* + +- Založte skolni-den.prproj ve složce, kde už leží záběry. +- Sekvence skolni-den: 1920 × 1080, 25 sn/s. +- Tři složky: Zabery, Zvuk, Grafika. Importujte aspoň jeden záběr a jednu hudbu do správných složek. +- Projekt uložte. Mp4 ještě nedělejte. + +> Tip: Když Premiere hlásí, že sekvence nesedí se záběrem, nechte sekvenci na 25. Je to zadání, ne chyba souboru. + +# Kvíz + +## Jakou příponu má projekt Premiere? +- mp4 +- [x] prproj +- psd +- svg +> prproj je projekt. mp4 vznikne až exportem. + +## Jaká je školní sekvence? +- 1280 × 720, 60 sn/s +- [x] 1920 × 1080, 25 sn/s +- 303 × 426 mm +- A4, 300 ppi +> Full HD a 25 snímků za sekundu. Milimetry patří plakátu, ne videu. + +## Kam v projektu patří hudba? +- Do složky Záběry +- [x] Do složky Zvuk +- Přímo do názvu sekvence +- Do PSD +> Záběry, Zvuk a Grafika se nemíchají. Jinak desátý soubor už nenajdete. + +## Co uděláte, když Premiere po přesunu složky video nevidí? +- Natočíte ho znovu +- [x] Propojíte médium a ukážete novou cestu +- Převedete projekt do JPG +- Změníte sekvenci na 72 ppi +> Projekt si pamatuje cestu. Propojit médium ji opraví, záběr se netočil znovu. diff --git a/content/prezentacni-grafika/3-rocnik/02-casova-osa-a-strih.en.md b/content/prezentacni-grafika/3-rocnik/02-casova-osa-a-strih.en.md new file mode 100644 index 0000000..5a9e1e6 --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/02-casova-osa-a-strih.en.md @@ -0,0 +1,6 @@ +--- +nazev: The timeline and the cut +popis: Cuts with no gaps, 45 seconds long. +nazev_cviceni: Quiz: the cut +nadpis_kvizu: Pick the tool and the length +--- diff --git a/content/prezentacni-grafika/3-rocnik/02-casova-osa-a-strih.md b/content/prezentacni-grafika/3-rocnik/02-casova-osa-a-strih.md new file mode 100644 index 0000000..5a047a2 --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/02-casova-osa-a-strih.md @@ -0,0 +1,79 @@ +--- +id: pg-3-strih +nazev: Časová osa a střih +popis: Střih záběrů bez mezer, délka 45 sekund. +nazev_cviceni: Kvíz: střih +nadpis_kvizu: Vyberte nástroj a délku +--- + +# Výklad + +## Stopy +*Obraz a zvuk* + +- V1 je první obrazová stopa, A1 první zvuková. Vyšší V leží vizuálně nad nižší. +- Záběr z kamery přinese obraz i zvuk. Na ose jsou spojené, dokud je nerozdělíte. +- Titulek patří na V2, ať nepřepíše záběr na V1. Sundáte ho a obraz pod ním zůstane. +- Mezera na ose je černé pole ve filmu. Mezi záběry být nemá. + +> Tip: Když přetáhnete jen obraz, držte Alt. Zvuk zůstane na místě, nebo se naopak nevezme. + +## Nástroje +*Žiletka* + +- V je výběr. C je žiletka, rozřízne záběr v místě přehrávací hlavy. +- I označí začátek, O konec. Čárka vloží výběr a zbytek odsunete. Tečka výběr přepíše. +- Delete smaže a nechá díru. Shift+Delete, nebo vyjmutí s uzavřením mezery, díru zatáhne. +- Mezerník přehrává. Šipky vlevo a vpravo jedou po střihu. + +> Tip: Po žiletce přepněte zpět na V. Jinak další klik zase řeže. + +## Co nechat +*45 sekund* + +- Ze surového záznamu nechte to, co nese větu nebo děj. Rozjezd a konec kamery pryč. +- Školní sestřih tohoto cvičení má 45 sekund. Kratší nestihne nic říct, delší se rozpadá. +- Střih je na pohybu nebo na začátku věty, ne uprostřed slova. +- Stejný záběr dvakrát po sobě nechte jen schválně. Jinak to vypadá jako chyba. + +> Tip: Délku sekvence čtete vpravo dole nebo na konci posledního záběru. 00:00:45:00 je 45 sekund. + +## Zadání: sestřih +*Cvičení* + +- Na ose skolni-den sestříhejte 45 sekund. Začátek je 00:00:00:00, konec 00:00:45:00. +- Mezi záběry není mezera. Černý snímek uprostřed je chyba. +- Aspoň čtyři různé záběry. Žádný kratší než jedna sekunda. +- Uložte projekt. Export až v pozdější lekci. + +> Tip: Když je konec 00:00:45:10, máte o 10 snímků navíc. U 25 sn/s je to necelá půlsekunda, zkraťte to. + +# Kvíz + +## Čím se záběr rozřízne? +- Nástrojem V +- [x] Žiletkou, klávesa C +- Exportem H.264 +- Krytím 70 % +> C je žiletka. V je výběr, tím se záběr posouvá, neřeže. + +## Jak dlouhý je sestřih v této lekci? +- [x] 45 sekund +- 8 sekund +- 4 minuty +- 303 mm +> 00:00:45:00. Titulky a export přijdou později, teď je čistý střih. + +## Co je chyba uprostřed sestřihu? +- Střih na začátku věty +- [x] Černá mezera mezi záběry +- Záběr delší než sekunda +- Stopa V1 +> Díra na ose je ve filmu černá. Po smazání ji zatáhněte. + +## Kam patří titulek, aby šel sundat bez ztráty záběru? +- Místo obrazu na V1 +- [x] Na stopu V2 nad záběrem +- Do složky Zvuk +- Do PSD plakátu +> V2 leží nad V1. Vypnete ho a záběr pod ním pokračuje. diff --git a/content/prezentacni-grafika/3-rocnik/03-tempo-a-prechody.en.md b/content/prezentacni-grafika/3-rocnik/03-tempo-a-prechody.en.md new file mode 100644 index 0000000..c124f37 --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/03-tempo-a-prechody.en.md @@ -0,0 +1,6 @@ +--- +nazev: Pace and transitions +popis: A hard cut as the default, one short dissolve. +nazev_cviceni: Quiz: transitions +nadpis_kvizu: Choose the length of the dissolve +--- diff --git a/content/prezentacni-grafika/3-rocnik/03-tempo-a-prechody.md b/content/prezentacni-grafika/3-rocnik/03-tempo-a-prechody.md new file mode 100644 index 0000000..132fc8e --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/03-tempo-a-prechody.md @@ -0,0 +1,79 @@ +--- +id: pg-3-prechody +nazev: Tempo a přechody +popis: Tvrdý střih jako základ, jedna krátká prolínačka. +nazev_cviceni: Kvíz: přechody +nadpis_kvizu: Zvolte délku prolínačky +--- + +# Výklad + +## Střih stačí +*Přechod* + +- Obyčejný střih je nejčistší. Přechod mezi každými dvěma záběry film zpomalí a vypadá jako šablona. +- Prolínačku berte na změnu času nebo na vstup titulku, ne na každý řez. +- Stírání, hvězdičky a stránkování ve školním dokumentu nemají co dělat. +- Jeden typ přechodu v celém videu. Druhý je už výstřednost. + +> Tip: Přechod přetáhnete z panelu Účinky mezi dva záběry. Dvojklik otevře délku. + +## Délka +*12 snímků* + +- Při 25 sn/s je sekunda 25 snímků. Půl sekundy je zhruba 12 snímků. +- Školní prolínačka má 12 snímků. Delší než sekunda už je nálada, ne střih. +- Přechod potřebuje materiál za řezem. Když záběr končí přesně na řezu, Premiere ho nemá čím prolnout a konec zmrazí. +- Proto neřežte záběr úplně na doraz, když víte, že na něj přijde prolínačka. + +> Tip: 12 snímků je 00:00:00:12. V časovém kódu jsou snímky až za poslední dvojtečkou. + +## Rychlost +*Polovina* + +- Pravý klik na záběr, Rychlost a trvání. 50 % ho přehraje poloviční rychlostí a dvakrát prodlouží. +- 100 % je normální tempo. 200 % je dvojnásobná rychlost a poloviční délka. +- Zpomalení použijte na jeden záběr, který má být čtený: vstup do dveří, pohled, detail. +- Zvuk zpomalený na 50 % je bručivý. U zpomaleného záběru ho oddělte a nechte původní, nebo ho ztichte. + +> Tip: Když má zůstat délka a změnit se jen tempo, hlídejte zaškrtnutí, které drží nebo nedrží délku záběru. + +## Zadání: jeden přechod, jedno tempo +*Cvičení* + +- V 45sekundovém sestřihu nechte tvrdé střihy. +- Jednu prolínačku dejte jen před titulek nebo mezi dvě místa. Délka 12 snímků. +- Jeden záběr zpomalte na 50 % a jeho zvuk nechte v původní rychlosti, nebo ho vypněte. +- Délka celku ať zůstane čitelná. Když se sekvence protáhne, zkraťte jiný záběr. + +> Tip: Přehrávání od prolínačky: mezerník. Když obraz na vteřinu zmrzne, záběru chybí přesah. + +# Kvíz + +## Jak dlouhá je školní prolínačka? +- [x] 12 snímků +- 12 sekund +- 4 minuty +- 3 mm +> 12 snímků při 25 sn/s je necelá půlsekunda. 12 sekund by bylo uspávací. + +## Kolik prolínaček má být v tomto cvičení? +- Mezi všemi záběry +- [x] Jedna +- Žádná nikdy, ani před titulkem +- Deset různých typů +> Základ je tvrdý střih. Jedna prolínačka má důvod, deset je šablona. + +## Co udělá rychlost 50 %? +- Záběr zrychlí na dvojnásobek +- [x] Přehrá ho poloviční rychlostí a prodlouží +- Ořízne ho na 50 px +- Převede ho do CMYK +> 50 % je polovina tempa. 200 % je dvojnásobná rychlost. + +## Proč prolínačka na konci záběru někdy zmrazí obraz? +- Sekvence je v PNG +- [x] Za řezem už není materiál, kterým by šla prolnout +- Chybí spadávka +- Hudba je moc potichu +> Přechod potřebuje snímky za střihem. Doraz na posledním snímku je nemá. diff --git a/content/prezentacni-grafika/3-rocnik/04-titulky.en.md b/content/prezentacni-grafika/3-rocnik/04-titulky.en.md new file mode 100644 index 0000000..0393aa0 --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/04-titulky.en.md @@ -0,0 +1,6 @@ +--- +nazev: Titles +popis: An opening and a closing, four seconds each. +nazev_cviceni: Quiz: titles +nadpis_kvizu: Fill in the time and the size +--- diff --git a/content/prezentacni-grafika/3-rocnik/04-titulky.md b/content/prezentacni-grafika/3-rocnik/04-titulky.md new file mode 100644 index 0000000..88bceec --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/04-titulky.md @@ -0,0 +1,79 @@ +--- +id: pg-3-titulky +nazev: Titulky +popis: Úvod a závěr, každé po čtyřech sekundách. +nazev_cviceni: Kvíz: titulky +nadpis_kvizu: Doplňte čas a velikost +--- + +# Výklad + +## Kde se píšou +*Grafika* + +- Titulek je vrstva na ose, ne text vypálený do záběru ve Photoshopu. +- Panel Základní grafika, nebo nástroj text nad obrazem v programu, založí textovou vrstvu. +- Úvodní titulek leží na začátku na V2. Závěrečný na konci. +- Písmo nechte jedno. Bílé na tmavém podkladu, nebo tmavé na světlém pruhu. + +> Tip: Když text píšete do záběru razítkem, už ho nepřepíšete. Vrstva textu jde opravit kdykoli. + +## Velikost a čas +*4 sekundy* + +- Nadpis Školní den má kolem 72 pt. Jméno autora kolem 36 pt. +- Každý titulek trvá 4 sekundy. Kratší nestačí přečíst, delší drží obraz zbytečně. +- 4 sekundy při 25 sn/s jsou 100 snímků. +- Mezi úvodním titulkem a prvním záběrem může být prolínačka 12 snímků z minulé lekce. + +> Tip: Délku textové vrstvy táhnete za konec na ose jako záběr. Číslo 00:00:04:00 je přesně čtyři sekundy. + +## Bezpečný okraj +*Nepřeříznout* + +- Text nepatří až k hraně obrazu. Od kraje nechte aspoň 80 px. +- Televize a některé přehrávače kraj oříznou. Co je 40 px od hrany, může zmizet. +- Dlouhý název rozdělte na dva řádky. Jeden řádek přes celou šířku je malý, nebo přeteče. +- Zarovnání na střed jen u titulku. U seznamu jmen nechte zarovnání vlevo. + +> Tip: V nastavení náhledu se dá zapnout bezpečná oblast. Vnitřní rámeček je pro text. + +## Zadání: úvod a závěr +*Cvičení* + +- Na začátek dejte 4 sekundy titulku Školní den, kolem 72 pt, aspoň 80 px od kraje. +- Na konec 4 sekundy se jménem autora, kolem 36 pt. +- Oba texty jsou na V2 a jdou přepsat. Nejsou vypálené v záběru. +- Projekt uložte. Délka sekvence teď smí být delší než 45 sekund o těchto 8 sekund. + +> Tip: Když titulek leží na V1 místo záběru, přesuňte ho na V2. Záběr pod ním ať pokračuje, nebo ať je pod úvodem prázdná plocha schválně. + +# Kvíz + +## Jak dlouho trvá úvodní titulek? +- [x] 4 sekundy +- 12 snímků +- 45 sekund +- 1 snímek +> Čtyři sekundy se dají přečíst. 12 snímků je délka prolínačky, ne titulku. + +## Jak velké je zhruba jméno autora? +- 8 pt +- [x] kolem 36 pt +- 300 pt +- 1600 px +> Nadpis je kolem 72 pt, jméno menší, kolem 36. Pořád čitelné na telefonu. + +## Jak daleko od kraje má text být? +- Až v posledních 10 px +- [x] Aspoň 80 px +- Ve spadávce 3 mm +- To je jedno, video se neořezává +> Kraj obrazu přehrávač někdy seřízne. 80 px text udrží uvnitř. + +## Na které stopě má titulek ležet? +- A1 +- [x] V2 +- Jen v JPG plakátu +- Místo zvuku +> V2 je nad obrazem. Titulek jde sundat a záběr zůstane. diff --git a/content/prezentacni-grafika/3-rocnik/05-zvuk.en.md b/content/prezentacni-grafika/3-rocnik/05-zvuk.en.md new file mode 100644 index 0000000..2bbc076 --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/05-zvuk.en.md @@ -0,0 +1,6 @@ +--- +nazev: Sound +popis: Speech, music under it and a one-second fade. +nazev_cviceni: Quiz: sound +nadpis_kvizu: Set the levels +--- diff --git a/content/prezentacni-grafika/3-rocnik/05-zvuk.md b/content/prezentacni-grafika/3-rocnik/05-zvuk.md new file mode 100644 index 0000000..a1779e7 --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/05-zvuk.md @@ -0,0 +1,79 @@ +--- +id: pg-3-zvuk +nazev: Zvuk +popis: Řeč, hudba pod ní a jedna sekunda náběhu. +nazev_cviceni: Kvíz: zvuk +nadpis_kvizu: Nastavte hladiny +--- + +# Výklad + +## Dvě hladiny +*Řeč a hudba* + +- Řeč je hlavní. Na ukazateli ať se drží kolem −6 dB a do červené nuly nelezete. +- Hudba sama o sobě kolem −15 dB. Pod řečí ji stáhněte ještě na −24 dB. +- Dva stejně hlasité zdroje se přebijí. Divák pak neslyší ani jeden pořádně. +- Ztlumení je klíčový snímek hlasitosti na klipu, ne jednou provždy na celou píseň, pokud pod ní jen občas mluvíte. + +> Tip: Pravý klik na zvukový klip a zobrazení klíčových snímků hlasitosti. Přidáte je perem na čáře. + +## Náběh +*1 sekunda* + +- Hudba nemá nastoupit v plné hlasitosti na prvním snímku. Náběh 1 sekundu. +- Konec hudby stejně tak: jednu sekundu do ticha, ať video neuskakuje. +- Náběh je taky klíčový snímek, z ticha na cílovou hladinu. +- Střih hudby uprostřed taktu je slyšet. Řežte v pauze nebo na začátku fráze. + +> Tip: 1 sekunda je 25 snímků. Na ose je to krátký úsek, ne celý titulek. + +## Střih zvuku napřed +*J-cut* + +- J-cut znamená, že zvuk dalšího záběru začne dřív než jeho obraz. +- Nejdřív je slyšet věta, až pak se obraz přepne na mluvčího. Střih je měkčí než tvrdý řez obou stop. +- Obraz a zvuk jednoho klipu rozdělíte pravým klikem, Zrušit propojení, a zvuk přetáhnete doleva. +- Ve školním videu stačí jeden takový střih. Nemusí být všude. + +> Tip: Když zvuk předběhne obraz o víc než půl sekundy, už to působí jako chyba synchronu. Držte se kolem 10 až 15 snímků. + +## Zadání: hudba pod řečí +*Cvičení* + +- Hudbu dejte pod sestřih. Mimo řeč kolem −15 dB, pod řečí −24 dB. +- Náběh i doběh hudby 1 sekunda. +- Řeč srovnejte kolem −6 dB. Červená nula na ukazateli je chyba. +- Jeden přechod mezi záběry udělejte jako J-cut: zvuk o kousek dřív než obraz. + +> Tip: Sluchátka odhalí hukot, reproduktor v učebně ho schová. Poslechněte si to jednou na sluchátkách. + +# Kvíz + +## Kde má být hudba, když zrovna někdo mluví? +- [x] Kolem −24 dB +- Na nule +- Vypnutá natrvalo i v místech bez řeči +- Hlasitější než řeč +> Řeč je kolem −6 dB. Hudba pod ní musí ustoupit, školní hladina je −24 dB. + +## Jak dlouhý je náběh hudby? +- [x] 1 sekunda +- 12 sekund +- 4 minuty +- Náběh se nedělá +> Jedna sekunda z ticha. Nástup v plné hlasitosti na prvním snímku je skok. + +## Co je J-cut? +- Obraz dalšího záběru začne dřív než jeho zvuk +- [x] Zvuk dalšího záběru začne dřív než jeho obraz +- Střih uprostřed slova +- Přechod hvězdičkou +> Nejdřív je slyšet, až pak je vidět. Opačné pořadí je L-cut. + +## Kam nesmí ukazatel řeči? +- K −6 dB +- [x] Do červené nuly +- Na stopu A1 +- Pod hudbu +> Nula je přebuzení. Řeč se drží pod ní, kolem −6 dB. diff --git a/content/prezentacni-grafika/3-rocnik/06-barva-ve-videu.en.md b/content/prezentacni-grafika/3-rocnik/06-barva-ve-videu.en.md new file mode 100644 index 0000000..59224c3 --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/06-barva-ve-videu.en.md @@ -0,0 +1,6 @@ +--- +nazev: Colour in video +popis: Exposure first, then contrast, saturation last. +nazev_cviceni: Quiz: video colour +nadpis_kvizu: Put the steps in order +--- diff --git a/content/prezentacni-grafika/3-rocnik/06-barva-ve-videu.md b/content/prezentacni-grafika/3-rocnik/06-barva-ve-videu.md new file mode 100644 index 0000000..c6f8264 --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/06-barva-ve-videu.md @@ -0,0 +1,79 @@ +--- +id: pg-3-barva +nazev: Barva ve videu +popis: Nejdřív expozice, potom kontrast, sytost až nakonec. +nazev_cviceni: Kvíz: barva videa +nadpis_kvizu: Seřaďte postup +--- + +# Výklad + +## Lumetri +*Jeden panel* + +- Barva záběru se dělá efektem Lumetri Color, ne exportem přes Photoshop. +- Efekt dejte na záběr, nebo na úpravu vrstvy nad stopou, ať se dá vypnout. +- Nejdřív srovnejte expozici, ať není záběr tmavý nebo přepálený. +- Pak kontrast, nakonec sytost. Sytá tmavá fotka se špatně zachraňuje. + +> Tip: Panel Barva Lumetri otevřete z nabídky Okno. Když efekt jen přetáhnete a panel zavřete, hodnoty stejně zůstanou na klipu. + +## Dva záběry vedle sebe +*Srovnat* + +- Střih mezi teplým a studeným záběrem je vidět víc než samotná barva. +- Druhý záběr přiblížte prvnímu: podobné stíny, podobná pleť. +- Pleť nenechte oranžovou. Když je sytost vysoko, uberte ji dřív, než přidáte teplou barvu. +- Jeden záběr smí být černobílý schválně. Sytost 0. Ostatní nechte barevné. + +> Tip: Oko porovná záběry, když je přehrajete přes střih. Zastavený snímek klame. + +## LUT až potom +*Míra* + +- LUT je hotová tabulka vzhledu. Dá se na záběr jako vzhled, ne jako první krok. +- Intenzitu LUT nechte kolem 50 %, když záběr přebarví moc. +- Dva různé LUT v jednom minutovém videu působí jako dvě školy. Stačí jeden, nebo žádný. +- Bez LUT, jen se srovnanou expozicí, je video často čistší. + +> Tip: LUT na přepálenou oblohu kresbu nevrátí. Nejdřív expozice, tabulka až na srovnaný obraz. + +## Zadání: srovnat a jeden bez barvy +*Cvičení* + +- Dva sousední záběry srovnejte Lumetri: nejdřív expozice, pak kontrast, sytost jen mírně, třeba +10. +- Jeden jiný záběr dejte na sytost 0, ať je černobílý schválně a krátce. +- LUT buď žádný, nebo jeden na intenzitě nejvýš 50 %. +- Efekt ať jde vypnout. Vypnuté Lumetri vrátí původní barvu záběru. + +> Tip: Když vypnutí efektu nic nezmění, upravovali jste jiný klip. Efekt je na tom, který je na ose vybraný. + +# Kvíz + +## V jakém pořadí se barva srovnává? +- Nejdřív sytost, pak třeba expozice +- [x] Nejdřív expozice, pak kontrast, sytost nakonec +- Nejdřív LUT, surový záběr se nechá +- Barva se ve videu nedělá +> Špatně světlý záběr sytost jen zhorší. Nejdřív světlo, potom síla barvy. + +## Jak uděláte jeden záběr černobílý? +- Smažete stopu V1 +- [x] Sytost na 0 +- Exportem do JPG +- Rychlostí 50 % +> Sytost 0 nechá jas a vezme barvu. Ostatní záběry zůstanou barevné. + +## Jak vysoko necháte intenzitu LUT, když barví moc? +- 100 % vždy +- [x] Kolem 50 % +- 0 %, ale LUT stejně všechno přebarví +- 300 % +> LUT je volba vzhledu, ne povinnost. Poloviční intenzita ho podrží na uzdě. + +## Čím se barva na záběru vypne? +- Smazáním projektu +- [x] Vypnutím efektu Lumetri +- Převodem sekvence na 72 ppi +- To nejde, export je trvalý už na ose +> Efekt na klipu jde vypnout. Dokud není video exportované, původní záběr zůstává. diff --git a/content/prezentacni-grafika/3-rocnik/07-export-videa.en.md b/content/prezentacni-grafika/3-rocnik/07-export-videa.en.md new file mode 100644 index 0000000..9644011 --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/07-export-videa.en.md @@ -0,0 +1,6 @@ +--- +nazev: Exporting the video +popis: H.264, Full HD, 25 fps and 10 Mb/s. +nazev_cviceni: Quiz: the video export +nadpis_kvizu: Pick the format and the bitrate +--- diff --git a/content/prezentacni-grafika/3-rocnik/07-export-videa.md b/content/prezentacni-grafika/3-rocnik/07-export-videa.md new file mode 100644 index 0000000..9675ca9 --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/07-export-videa.md @@ -0,0 +1,79 @@ +--- +id: pg-3-export +nazev: Export videa +popis: H.264, Full HD, 25 sn/s a 10 Mb/s. +nazev_cviceni: Kvíz: export videa +nadpis_kvizu: Vyberte formát a datový tok +--- + +# Výklad + +## Formát +*H.264* + +- Odevzdává se MP4 s kodekem H.264. Otevře ho telefon, projektor i Windows. +- Formát projektu prproj nikdo bez Premiere nepustí. Není to výstup. +- Sekvence 1920 × 1080 a 25 sn/s se do exportu přenese. Neměňte ji při exportu na 1280 × 720, pokud to zadání nechce. +- Zvuk nechte v exportu zapnutý. Němý výstup je často jen odškrtnutý zvuk, ne záměr. + +> Tip: Soubor > Export > Média. Zkratka Ctrl+M. Náhled vpravo ukáže první snímek, ne celý střih. + +## Datový tok +*10 Mb/s* + +- Cílový datový tok 10 Mb/s stačí na školní Full HD. Vyšší soubor zbytečně naroste. +- 1 Mb/s už na 1080p kostkuje, hlavně v pohybu. 10 je klidná střední hodnota. +- Nastavení zdroje, nebo shoda se sekvencí, podrží 25 sn/s a rozměr. +- Dva průchody export zpřesní, ale trvá déle. Na školní video stačí jeden. + +> Tip: Mb/s je megabit za sekundu, ne megabajt. Minutové video při 10 Mb/s má zhruba 75 MB. + +## Rozsah +*Celá sekvence* + +- Exportujte celou sekvenci, ne jen výběr mezi body In a Out, pokud jste je nechali uprostřed. +- Zaškrtnutí, které exportuje jen vstup až výstup, umí oříznout film na pět sekund. +- Název souboru skolni-den.mp4. Cesta ať vede do složky projektu, ne na plochu. +- Po exportu soubor pusťte mimo Premiere. V náhledu programu chybu často nevidíte. + +> Tip: Když je film moc krátký, otevřete body In a Out na ose. Ctrl+Shift+X je zruší. + +## Zadání: první mp4 +*Cvičení* + +- Exportujte skolni-den.mp4, H.264, 1920 × 1080, 25 sn/s, cílový tok 10 Mb/s. +- Zvuk je v souboru. Úvodní titulek je hned na začátku, ne po deseti sekundách černé. +- Projekt prproj nechte vedle mp4. Mp4 samo o sobě nejde znovu střihnout po vrstvách. +- Soubor přehrajte celý. Jednou. Zastavte se na titulku, na prolínačce a na konci hudby. + +> Tip: Když přehrávač hlásí, že soubor nejde otevřít, export nedoběhl. V Premiere je fronta, podívejte se, jestli neskončil chybou. + +# Kvíz + +## Který soubor se odevzdává jako video? +- prproj +- [x] mp4 v H.264 +- psd +- svg +> MP4 otevře i člověk bez Premiere. Projekt je zdroj. + +## Jaký datový tok má školní Full HD? +- [x] 10 Mb/s +- 1 Mb/s +- 300 ppi +- 72 Mb/s vždy +> 10 Mb/s je na 1080p25 dost. 1 Mb/s se v pohybu rozpadá. + +## Co zkontrolujete, když je export dlouhý jen pár sekund? +- Spadávku +- [x] Jestli se neexportoval jen úsek mezi In a Out +- Režim CMYK +- Krytí pruhu +> Body In a Out omezí export na výběr. Zrušte je a exportujte celou sekvenci. + +## Proč nestačí odevzdat jen prproj? +- Premiere ho neumí uložit +- [x] Bez Premiere ho nikdo nepřehraje a média mohou chybět +- Nemá 25 sn/s +- Je to totéž co mp4 +> Projekt je odkaz na střih. Film je až mp4. diff --git a/content/prezentacni-grafika/3-rocnik/08-zadani-video.en.md b/content/prezentacni-grafika/3-rocnik/08-zadani-video.en.md new file mode 100644 index 0000000..4bacdcf --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/08-zadani-video.en.md @@ -0,0 +1,6 @@ +--- +nazev: Assignment: a short video +popis: 40 to 60 seconds, titles, sound and one J-cut. +nazev_cviceni: Quiz: the short video +nadpis_kvizu: Check the length and the export +--- diff --git a/content/prezentacni-grafika/3-rocnik/08-zadani-video.md b/content/prezentacni-grafika/3-rocnik/08-zadani-video.md new file mode 100644 index 0000000..f415877 --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/08-zadani-video.md @@ -0,0 +1,79 @@ +--- +id: pg-3-zadani-video +nazev: Zadání: krátké video +popis: 40 až 60 sekund, titulky, zvuk a jeden J-cut. +nazev_cviceni: Kvíz: krátké video +nadpis_kvizu: Zkontrolujte délku a export +--- + +# Výklad + +## Délka a obsah +*40 až 60 s* + +- Hotové video trvá 40 až 60 sekund včetně titulků. +- Téma je školní den: místo, lidé, jedna věta o tom, co se děje. +- Úvodní titulek Školní den trvá 4 sekundy. Závěrečný se jménem taky 4 sekundy. +- Aspoň čtyři záběry. Žádný pod jednu sekundu, kromě případného černobílého detailu. + +> Tip: Délku čtete na konci posledního titulku. Titulky se do limitu počítají. + +## Střih a obraz +*Pravidla* + +- Mezi záběry nejsou černé mezery. Střihy jsou tvrdé. +- Jedna prolínačka, 12 snímků, nejlépe mezi titulkem a záběrem. +- Jeden záběr smí být na 50 % rychlosti. Jeden smí mít sytost 0. +- Barva je srovnaná Lumetri: expozice, kontrast, sytost. Nejvýš jeden LUT na 50 %. + +> Tip: V2 je na titulky. Obrazové záběry ať zůstanou na V1, ať se ve stopách neztratíte. + +## Zvuk +*Hladiny* + +- Řeč kolem −6 dB, bez červené nuly. +- Hudba mimo řeč −15 dB, pod řečí −24 dB. Náběh a doběh 1 sekunda. +- Jeden J-cut: zvuk dalšího záběru začne dřív než obraz. +- Hudba nekončí uprostřed slova ani uprostřed taktu, pokud to jde střihnout jinak. + +> Tip: Poslechněte si export, ne jen osu. Export umí zvuk vynechat, když je odškrtnutý. + +## Odevzdání +*Dva soubory* + +- skolni-den.prproj ve složce s médií, složky Zabery, Zvuk, Grafika. +- skolni-den.mp4, H.264, 1920 × 1080, 25 sn/s, 10 Mb/s. +- Mp4 pusťte od začátku do konce mimo Premiere. +- Když média přesouváte, nejdřív složku celou. Rozbitý odkaz je neodevzdaná práce. + +> Tip: Na začátku mp4 má být titulek, ne černá. Černá znamená, že sekvence začíná později než export. + +# Kvíz + +## Jak dlouhé je hotové video i s titulky? +- Přesně 45 sekund bez titulků +- [x] 40 až 60 sekund +- 8 sekund +- 4 minuty +> Titulky se počítají. Pod 40 sekund téma nestačí, nad minutu se školní sestřih rozpadá. + +## Jak dlouhé jsou oba titulky? +- 12 snímků +- [x] 4 sekundy každý +- 1 sekunda +- Dokud hraje hudba +> Čtyři sekundy na úvod, čtyři na jméno. 12 snímků je prolínačka. + +## Kam patří hudba v okamžiku, kdy mluví člověk? +- [x] −24 dB +- 0 dB +- Stejně nahlas jako řeč +- Hudba se pod řeč nedává nikdy, ani ztišená +> Řeč zůstává kolem −6 dB. Hudba pod ní ustoupí na −24 dB. + +## Jaký je export? +- prproj jako jediný soubor +- [x] mp4, H.264, 1920 × 1080, 25 sn/s, 10 Mb/s +- JPG 1600 px +- PDF se spadávkou +> Film je mp4. Projekt se odevzdává k němu, ne místo něj. diff --git a/content/prezentacni-grafika/4-rocnik/01-illustrator.en.md b/content/prezentacni-grafika/4-rocnik/01-illustrator.en.md new file mode 100644 index 0000000..29c372b --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/01-illustrator.en.md @@ -0,0 +1,6 @@ +--- +nazev: Illustrator: the artboard +popis: Curves, the V, A and P tools, an artboard for the screen. +nazev_cviceni: Quiz: Illustrator +nadpis_kvizu: Name the tool +--- diff --git a/content/prezentacni-grafika/4-rocnik/01-illustrator.md b/content/prezentacni-grafika/4-rocnik/01-illustrator.md new file mode 100644 index 0000000..d6171c5 --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/01-illustrator.md @@ -0,0 +1,79 @@ +--- +id: pg-4-illustrator +nazev: Illustrator: plocha a nástroje +popis: Křivky, nástroje V, A a P, plátno pro obrazovku. +nazev_cviceni: Kvíz: Illustrator +nadpis_kvizu: Poznejte nástroj +--- + +# Výklad + +## Proč Illustrator +*Křivka* + +- Illustrator kreslí křivky. Logo z něj vytisknete na vizitku i na banner a hrana zůstane hladká. +- Photoshopové logo z malého PNG sem nepatří. Vektor se tu staví znovu, nebo se otevře SVG a AI. +- Soubor je AI. Na výměnu s jiným programem se hodí i SVG a PDF. +- Na fotku Illustrator není. Fotka sem patří jen jako podklad, který pak skryjete. + +> Tip: Když v náhledu na 800 % uvidíte pixely, není to křivka. Je to vložená bitmapa. + +## Tři nástroje +*V, A, P* + +- V je výběr. Posouvá a mění celý objekt. +- A je přímý výběr. Táhne jeden kotevní bod, zbytek tvaru nechá. +- P je pero. Klik klade body, tah klade křivku. Pero je hlavní kreslicí nástroj. +- Na první cvičení pero nepotřebujete celé. Obdélník a elipsa stačí. + +> Tip: Na Macu jsou zkratky stejné. Přepínají se jednou klávesou, bez Cmd. + +## Plátno +*1920 × 1080* + +- Nový dokument pro obrazovku: 1920 × 1080 px, RGB, jeden artboard. +- Pro tisk se později založí milimetrový artboard. Teď je to obrazovka, ať sedí s Premiere a Photoshopem. +- Artboard není vrstva. Je to papír. Objekty mimo něj se neexportují, ale v souboru jsou. +- Bílé pozadí artboardu není objekt. Když chcete barvu v pozadí, nakreslete obdélník. + +> Tip: Více artboardů přijde u loga ve variantách. Zatím jeden. + +## Zadání: dva tvary +*Cvičení* + +- Dokument znacka-podklad.ai, 1920 × 1080 px, RGB. +- Nakreslete jeden obdélník a jednu elipsu. Každý ať má výplň a žádný tah, nebo tah 2 pt schválně. +- Elipsu vyberte nástrojem A a vytáhněte jeden kotevní bod, ať není pravidelná. +- Uložte AI. Do JPG to zatím nepřevádějte. + +> Tip: Výplň a tah jsou dole v panelu nástrojů. Prázdná výplň, červený přeškrtnutý čtverec, objekt neukáže, dokud nemá tah. + +# Kvíz + +## Co dělá nástroj A? +- Posouvá celý objekt +- [x] Táhne jeden kotevní bod +- Píše text ve Photoshopu +- Stříhá video +> A je přímý výběr bodu. V posouvá celý tvar. + +## Který nástroj kreslí křivku po bodech? +- V +- [x] P, pero +- C v Premiere +- Kapátko I +> Pero je P. Žiletka C patří do Premiere, sem ne. + +## Jak velký je školní dokument pro obrazovku? +- [x] 1920 × 1080 px, RGB +- A4, CMYK, 300 ppi jako jediná možnost +- 303 × 426 mm hned +- 80 × 80 px +> Obrazovka je Full HD a RGB. Tiskový artboard přijde, až bude značka na papír. + +## Kdy v náhledu poznáte vloženou bitmapu? +- Na 800 % zůstane hrana hladká +- [x] Na 800 % se objeví pixely +- Soubor má příponu AI, takže pixely nemá nikdy +- Podle stopy V2 +> Křivka se nezvětší do čtverečků. Bitmapa ano. diff --git a/content/prezentacni-grafika/4-rocnik/02-logo.en.md b/content/prezentacni-grafika/4-rocnik/02-logo.en.md new file mode 100644 index 0000000..2a00d3a --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/02-logo.en.md @@ -0,0 +1,6 @@ +--- +nazev: Curves and a logo +popis: A mark made of three shapes, a full version and one colour. +nazev_cviceni: Quiz: the logo +nadpis_kvizu: Check the versions and the export +--- diff --git a/content/prezentacni-grafika/4-rocnik/02-logo.md b/content/prezentacni-grafika/4-rocnik/02-logo.md new file mode 100644 index 0000000..31c6033 --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/02-logo.md @@ -0,0 +1,79 @@ +--- +id: pg-4-logo +nazev: Křivky a logo +popis: Značka ze tří tvarů, plná verze a jedna barva. +nazev_cviceni: Kvíz: logo +nadpis_kvizu: Zkontrolujte verze a export +--- + +# Výklad + +## Málo bodů +*Čistá křivka* + +- Logo je jednoduché. Tři tvary stačí: třeba kniha, paprsek a kruh. +- Každý tvar ať má co nejmíň kotevních bodů. Deset bodů na kružnici je zbytečných, elipsa je jeden objekt. +- Tah převeďte na obrys, až když má být tloušťka součástí tvaru. Do té doby ať je tah tahem a jde změnit. +- Text názvu školy nechte jako text, dokud nemáte vybrané písmo. Pak ho můžete převést na křivky. + +> Tip: Objekt > Cesta > Obrysy tahu, nebo Písmo > Vytvořit obrysy. Až v kopii. Původní text ať jde přepsat. + +## Dvě verze +*Plná a jedna barva* + +- Plná verze má barvy školy, nejvýš dvě plus bílou nebo černou. +- Jednobarevná verze je celá černá. Hodí se do razítka, do raznice a na světlý papír. +- Obě verze jsou dva artboardy v jednom AI, ne dva náhodné JPG. +- Na bílém i na černém podkladu musí být značka čitelná. Bílá značka na bílé není verze, je to chyba. + +> Tip: Třetí artboard si nechte na černé pozadí a bílou značku, když ji škola bude dávat na tričko. + +## Export +*SVG a PDF* + +- SVG je pro web. Křivky v něm zůstanou, stránka ho zvětší. +- PDF je pro tisk a pro vložení do InDesignu. +- JPG z loga dělejte jen jako náhled, velký aspoň 1600 px. Jako jediný zdroj je zakázaný. +- Názvy: logo.svg, logo.pdf, logo-cerna.svg. + +> Tip: Při exportu SVG nechte text v obrysech, pokud má značku otevřít někdo bez vašeho písma. + +## Zadání: značka školy +*Cvičení* + +- Nakreslete značku ze tří tvarů a názvu školy. +- Artboard plné verze a artboard černé verze. +- Exportujte SVG a PDF u obou. JPG jen jako náhled 1600 px, navíc. +- Soubor logo.ai si nechte. V něm musí jít značku přebarvit. + +> Tip: Tři tvary, ne třicet. Když značku nepřečtete na 16 px, je moc podrobná. + +# Kvíz + +## Z kolika tvarů je školní značka? +- [x] Ze tří +- Z jedné fotky +- Z dvaceti přechodů +- Z videa +> Tři tvary se pamatují a vejdou se na vizitku. Fotka není logo. + +## Která verze se hodí do razítka? +- Plnobarevná s přechodem +- [x] Jednobarevná černá +- JPG 80 px +- Černobílé video +> Razítko neumí přechod. Černá verze se vytiskne jednou barvou. + +## Který export podrží křivky na webu? +- JPG +- [x] SVG +- MP4 +- prproj +> SVG je vektor. JPG je bitmapa a při zvětšení se rozmaže. + +## Proč nestačí odevzdat jen JPG loga? +- JPG nejde otevřít +- [x] Nejsou v něm křivky a malý soubor nejde zvětšit +- JPG je totéž co AI +- Illustrator JPG neuloží +> Zdroj je AI. SVG a PDF jsou výstupy. JPG je jen náhled. diff --git a/content/prezentacni-grafika/4-rocnik/03-after-effects.en.md b/content/prezentacni-grafika/4-rocnik/03-after-effects.en.md new file mode 100644 index 0000000..dcfedbb --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/03-after-effects.en.md @@ -0,0 +1,6 @@ +--- +nazev: After Effects: a composition +popis: Eight seconds, Full HD, 25 fps and the logo layers. +nazev_cviceni: Quiz: the composition +nadpis_kvizu: Set up the composition +--- diff --git a/content/prezentacni-grafika/4-rocnik/03-after-effects.md b/content/prezentacni-grafika/4-rocnik/03-after-effects.md new file mode 100644 index 0000000..2d9786e --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/03-after-effects.md @@ -0,0 +1,79 @@ +--- +id: pg-4-kompozice +nazev: After Effects: kompozice +popis: Osm sekund, Full HD, 25 sn/s a vrstvy loga. +nazev_cviceni: Kvíz: kompozice +nadpis_kvizu: Založte kompozici +--- + +# Výklad + +## Kompozice +*Jako sekvence* + +- Kompozice v After Effects je plátno v čase. Má rozměr, snímkovou frekvenci a délku. +- Školní úvod: 1920 × 1080, 25 sn/s, délka 8 sekund. +- 8 sekund je 200 snímků. Titulek z Premiere trval 4, tady je čas i na pohyb. +- Projekt má příponu aep. Není to mp4. + +> Tip: Nová kompozice je Ctrl+N. Délku pište 0:00:08:00, ne 8 snímků. + +## Vrstvy +*Stejný princip* + +- Horní vrstva překrývá spodní, jako ve Photoshopu. +- Logo z AI vložte jako kompozici nebo jako vrstvy, ať jde každý tvar animovat zvlášť. +- PSD s vrstvami importujte jako kompozici, ne jako sloučený snímek. +- Vrstvy pojmenujte. Tvar 1 a Tvar 2 se při animaci pletou. + +> Tip: Když je logo jedna sloučená vrstva, jednotlivý paprsek nerozhýbáte. Importujte znovu s vrstvami. + +## Kotva +*Kolem čeho se to točí* + +- Kotva je bod, kolem kterého vrstva roste a otáčí se. +- U loga má sedět uprostřed značky, ne v rohu plátna. +- Nástroj kotva, nebo dvojklik na ikonu kotvy v panelu, ji srovná na střed vrstvy. +- Špatná kotva je nejčastější důvod, proč logo přijede z boku, i když jste chtěli zvětšení. + +> Tip: Vrstvu si najeďte na začátek času, srovnejte kotvu, a teprve potom dávejte klíčové snímky. + +## Zadání: prázdný úvod +*Cvičení* + +- Projekt uvod.aep, kompozice uvod, 1920 × 1080, 25 sn/s, 8 sekund. +- Importujte logo tak, aby tvary byly samostatné vrstvy, a pojmenujte je. +- Kotvu každého tvaru dejte na jeho střed. +- Zatím nic neanimujte. Uložte aep. Mp4 z After Effects ještě nedělejte. + +> Tip: Délku kompozice přečtete nahoře v časové ose. Když končí na 10 sekundách, zkraťte ji na 8. + +# Kvíz + +## Jak dlouhá je školní kompozice úvodu? +- [x] 8 sekund +- 45 sekund +- 4 snímky +- 303 mm +> Osm sekund stačí na nástup loga a textu. Delší úvod sežere krátké video. + +## Jaká je snímková frekvence? +- [x] 25 sn/s +- 300 ppi +- 72 sn/s vždy +- 1 sn/s +> Stejně jako Premiere, 25. Jinak se úvod ve filmu cuká nebo přepočítává. + +## Kam má sedět kotva loga? +- Do levého horního rohu plátna +- [x] Na střed značky +- Mimo kompozici +- Na stopu A1 +> Od kotvy se počítá zvětšení a otočení. V rohu plátna značka odjíždí. + +## Jak se má importovat logo z Illustratoru? +- Jako jeden JPG +- [x] Po vrstvách, ať jde každý tvar zvlášť +- Jako zvuk +- Sloučené a hned exportované +> Sloučená vrstva se hýbe jen celá. Tvary potřebují vlastní vrstvy. diff --git a/content/prezentacni-grafika/4-rocnik/04-klicove-snimky.en.md b/content/prezentacni-grafika/4-rocnik/04-klicove-snimky.en.md new file mode 100644 index 0000000..8d9a26f --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/04-klicove-snimky.en.md @@ -0,0 +1,6 @@ +--- +nazev: Keyframes +popis: The logo arrives in one second, and the motion eases. +nazev_cviceni: Quiz: keyframes +nadpis_kvizu: Fill in the times +--- diff --git a/content/prezentacni-grafika/4-rocnik/04-klicove-snimky.md b/content/prezentacni-grafika/4-rocnik/04-klicove-snimky.md new file mode 100644 index 0000000..c586b4b --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/04-klicove-snimky.md @@ -0,0 +1,79 @@ +--- +id: pg-4-klice +nazev: Klíčové snímky +popis: Nástup loga za jednu sekundu a zjemnění pohybu. +nazev_cviceni: Kvíz: klíčové snímky +nadpis_kvizu: Doplňte časy +--- + +# Výklad + +## Dva snímky +*Od a do* + +- Klíčový snímek je hodnota v čase: pozice, měřítko nebo krytí. +- Na čase 0 má logo měřítko 0 %. Na čase 1 sekunda má 100 %. +- Mezi těmito dvěma snímky After Effects pohyb dopočítá. +- Jeden snímek nic neanimuje. Bez druhého hodnoty není kam dojít. + +> Tip: Stopky vedle vlastnosti v časové ose založí snímek. Druhý vznikne, když v jiném čase změníte hodnotu. + +## Zjemnění +*F9* + +- Rovnoměrný pohyb vypadá strojově. Easy Ease, klávesa F9, začátek a konec zpomalí. +- F9 mačkejte, když jsou snímky vybrané. Jinak se nezmění nic. +- Na nástup loga Easy Ease patří. Na střih záběru v Premiere ne, to je jiný program. +- Krytí textu nechte běžet taky mezi dvěma snímky, ne skokem z 0 na 100. + +> Tip: Změněný snímek má po F9 jinou ikonu, přesýpací, ne kosočtverec. Podle toho poznáte, že zjemnění sedí. + +## Text +*1 + 4 + 1* + +- Text se objeví za 1 sekundu, zůstane 4 sekundy a zmizí za 1 sekundu. +- To je 6 sekund textu. Logo nastupuje v první sekundě, text může začít po ní. +- Celá kompozice má 8 sekund, takže na konci zůstanou 2 sekundy klidu nebo doběhu. +- Text pište v After Effects. Nevypalujte ho do PSD, pokud ho chcete ještě posunout. + +> Tip: Čtyři sekundy klidu jsou stejné jako titulek v Premiere. Divák čte stejně pomalu. + +## Zadání: nástup +*Cvičení* + +- Logo z měřítka 0 % na 100 % za první sekundu. Oba snímky s F9. +- Název Školní den: nástup 1 sekunda, klid 4 sekundy, odchod 1 sekunda. +- Kotvy jsou uprostřed. Logo neodjíždí z rohu. +- Uložte uvod.aep. Export mp4 až po kontrole přehrávání, délka pořád 8 sekund. + +> Tip: Mezerník přehrává. Když logo skočí, chybí druhý snímek nebo je na stejném čase jako první. + +# Kvíz + +## Jaké měřítko má logo v čase 0 a v čase 1 sekunda? +- 100 % a 100 % +- [x] 0 % a 100 % +- 50 % a 50 % +- 300 ppi +> Z nuly na plnou velikost za sekundu. Bez dvou snímků se nic nepohne. + +## Čím se nástup zjemní? +- [x] F9, Easy Ease +- Ctrl+J +- Sytostí 0 +- Spadávkou +> F9 na vybraných snímcích zpomalí rozjezd a dojezd. Pohyb přestane být pravítko. + +## Jak je rozložený text? +- [x] 1 s nástup, 4 s klid, 1 s odchod +- 12 snímků celkem +- 8 s nástup a hned pryč +- Jen vypálený v JPG +> Jedna sekunda se objeví, čtyři se čte, jedna zmizí. Dohromady šest sekund. + +## Proč logo odjíždí z rohu, i když měníte jen měřítko? +- Sekvence je 30 sn/s +- [x] Kotva není uprostřed značky +- Chybí CMYK +- F9 je zapnuté +> Měřítko roste od kotvy. Kotva v rohu plátna žene značku přes obraz. diff --git a/content/prezentacni-grafika/4-rocnik/05-indesign.en.md b/content/prezentacni-grafika/4-rocnik/05-indesign.en.md new file mode 100644 index 0000000..1eb4419 --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/05-indesign.en.md @@ -0,0 +1,6 @@ +--- +nazev: InDesign: a page +popis: A two-page A4 leaflet, a master and a placed image. +nazev_cviceni: Quiz: InDesign +nadpis_kvizu: Recognise the master and the link +--- diff --git a/content/prezentacni-grafika/4-rocnik/05-indesign.md b/content/prezentacni-grafika/4-rocnik/05-indesign.md new file mode 100644 index 0000000..1c2d5a1 --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/05-indesign.md @@ -0,0 +1,79 @@ +--- +id: pg-4-indesign +nazev: InDesign: stránka +popis: Dvoustránkový leták A4, předloha a vložený obraz. +nazev_cviceni: Kvíz: InDesign +nadpis_kvizu: Poznejte předlohu a odkaz +--- + +# Výklad + +## Stránka, ne plátno +*A4* + +- InDesign skládá stránku: text, obraz a logo v rámcích. +- Leták je A4, 210 × 297 mm. Spadávka 3 mm se nastaví u dokumentu, stránka zůstane A4. +- Na rozdíl od Photoshopu se tu plátno nezvětšuje na 216 × 303. Přesah je spadávka mimo stránku. +- Dvě strany: přední s názvem akce, zadní s krátkým programem dne. + +> Tip: Nový dokument: záměr Tisk, A4, dvě stránky, spadávka 3 mm všude. + +## Rámce +*Text a obraz* + +- Text žije v textovém rámci. Obraz v rámci obrazu. Nejsou to vrstvy Photoshopu. +- Obraz se vkládá Soubor > Umístit. Nevkládejte ho přes schránku jako snímek obrazovky. +- Umístěný PSD nebo JPG je odkaz. Když opravíte PSD a uložíte, InDesign ho umí aktualizovat. +- Vložený obraz, který v souboru uvízne bez odkazu, se po opravě fotky sám nezmění. + +> Tip: Panel Odkazy ukáže, jestli je soubor aktuální, nebo chybí. Chybějící odkaz je červeně. + +## Předloha +*Číslo strany* + +- Předloha, master, je stránka pod ostatními. Co je na ní, opakuje se. +- Na předlohu dejte malé logo a číslo strany. Na běžnou stránku už jen obsah. +- Prvek z předlohy na jedné straně odemknete Ctrl+Shift+klik, když ho máte výjimečně posunout. +- Číslo strany je znak čísla stránky, ne ručně napsaná jednička. Druhá strana se pak očísluje sama. + +> Tip: Předloha je nahoře v panelu Stránky. Dvojklik ji otevře. Dvojklik na stránku 1 vás vrátí do dokumentu. + +## Zadání: dvě strany +*Cvičení* + +- letak.indd, A4, dvě strany, spadávka 3 mm, okraje aspoň 12 mm. +- Předloha: logo a automatické číslo strany. +- Strana 1: název Školní den, datum 12. 6., umístěná fotka jako odkaz. +- Strana 2: tři řádky programu. Uložte INDD a export nechte na pozdější lekci, nebo udělejte náhled PDF. + +> Tip: Fotku nejdřív upravte ve Photoshopu a uložte PSD. Do InDesignu ji umístěte, nekopírujte ji znovu. + +# Kvíz + +## Jak je v InDesignu nastavená spadávka? +- Stránka se zvětší na 303 × 426 mm +- [x] Stránka zůstane A4 a spadávka 3 mm je navíc +- Spadávka se v sazbě nedělá +- 72 ppi +> A4 je čistý format. 3 mm přesah je vlastnost dokumentu, ne jiný rozměr stránky. + +## Jak se má dostat fotka do letáku? +- Snímkem obrazovky +- [x] Soubor > Umístit, jako odkaz +- Vypálením do předlohy jednou provždy +- Jako mp4 +> Odkaz jde po opravě PSD aktualizovat. Snímek obrazovky je druhá, horší fotka. + +## Co patří na předlohu? +- Celý program dne, na každé straně jiný +- [x] Logo a automatické číslo strany +- Časová osa Premiere +- Maska z Photoshopu +> Předloha je to, co se opakuje. Program dne je obsah konkrétní strany. + +## Proč se číslo strany nepíše ručně jako 1? +- InDesign čísla neumí +- [x] Znak čísla stránky se na druhé straně změní sám +- Ruční jednička je vektor +- Kvůli 25 sn/s +> Znak čísla stránky čte, na které straně je. Ruční 1 zůstane 1 i na straně 2. diff --git a/content/prezentacni-grafika/4-rocnik/06-propojeni.en.md b/content/prezentacni-grafika/4-rocnik/06-propojeni.en.md new file mode 100644 index 0000000..362f549 --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/06-propojeni.en.md @@ -0,0 +1,6 @@ +--- +nazev: Linking the programs +popis: The Illustrator logo in After Effects, and the opener in Premiere without a middle export. +nazev_cviceni: Quiz: linking +nadpis_kvizu: Pick what updates +--- diff --git a/content/prezentacni-grafika/4-rocnik/06-propojeni.md b/content/prezentacni-grafika/4-rocnik/06-propojeni.md new file mode 100644 index 0000000..0c545f2 --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/06-propojeni.md @@ -0,0 +1,79 @@ +--- +id: pg-4-propojeni +nazev: Propojení programů +popis: Logo z Illustratoru v After Effects a úvod v Premiere bez mezivýstupu. +nazev_cviceni: Kvíz: propojení +nadpis_kvizu: Vyberte, co se aktualizuje +--- + +# Výklad + +## Jeden zdroj +*Odkaz* + +- Logo má jeden zdroj, logo.ai. Photoshop, After Effects i InDesign ať na něj odkazují. +- Když značku přebarvíte v AI a uložíte, odkazy ji mohou načíst znovu. Kopírování JPG to neumí. +- JPG 80 px vložené do čtyř programů jsou čtyři různé značky. Jedna se opraví, tři zůstanou staré. +- Pojmenujte zdroj jednou. logo-final-final2.ai je cesta, jak odevzdat špatnou verzi. + +> Tip: V After Effects se odkaz obnoví v panelu projektu. V InDesignu v panelu Odkazy. + +## Dynamic Link +*AE do Premiere* + +- Dynamic Link vloží kompozici After Effects na časovou osu Premiere bez mezivýstupu mp4. +- Když v úvodu posunete text, Premiere po uložení aep ukáže novou verzi. +- Oba programy mají být stejná generace. Starší Premiere nový aep často neotevře. +- Soubory aep a prproj mějte ve stejné složce. Odkaz jinak spadne při přesunu jen jednoho z nich. + +> Tip: V Premiere: Soubor > Adobe Dynamic Link > Importovat kompozici After Effects. Na osu přijde jako záběr. + +## Co nepropojovat +*Kopie* + +- Zvuk do Illustratoru nepatří. Video do InDesignu taky ne, kromě náhledu, který tu neděláme. +- Plakát v PSD zůstává tisk. Do videa z něj berte jen logo nebo krátký náhled, ne celou spadávku se značkami. +- Ořezové značky plakátu ve videu jsou chyba. Ty má jen tiskové PDF. +- Když Dynamic Link nejde, exportujte úvod jako mp4 v ProRes nebo aspoň v kvalitním H.264 a ten vložte. Je to kopie, už se sama neopraví. + +> Tip: ProRes je mezisoubor, velký a kvalitní. H.264 je menší a už jednou stlačený. Do dalšího exportu je horší. + +## Zadání: úvod na ose +*Cvičení* + +- V Premiere na začátek sekvence vložte kompozici uvod z After Effects přes Dynamic Link. +- Délka ať sedí: 8 sekund úvodu, pak pokračuje sestřih. +- Změňte v AI jednu barvu loga, uložte, a obnovte odkaz až do videa. Když se barva nezmění, máte někde JPG. +- Uložte prproj i aep. Mp4 celého filmu exportujte znovu až na konci, ne po každém kroku. + +> Tip: Když se úvod na ose tváří jako offline, chybí aep nebo je jiná verze programu. Neexportujte přes to černou. + +# Kvíz + +## Kde má žít značka, aby šla opravit všude? +- [x] V jednom logo.ai +- Ve čtyřech různých JPG +- Jen vypálená ve videu +- V titulku Premiere jako jediném místě +> Jeden zdroj. Ostatní programy na něj odkazují. + +## Co dělá Dynamic Link? +- Převede plakát do CMYK +- [x] Vloží kompozici After Effects do Premiere bez mezivýstupu +- Smaže vrstvy v PSD +- Nahradí InDesign +> Úprava v aep se po uložení ukáže na ose. Mp4 mezi tím není potřeba. + +## Co do videa z plakátu nepatří? +- Logo +- [x] Ořezové značky +- Název akce +- Barva značky +> Značky jsou pro tiskárnu. Ve snímku videa vypadají jako omylem vyfocený tiskový arch. + +## Kdy je mp4 z After Effects horší než Dynamic Link? +- [x] Když chcete, aby se oprava textu sama propsala do Premiere +- Když video má 25 sn/s +- Když je úvod dlouhý 8 sekund +- Mp4 je vždycky zdroj +> Mp4 je kopie. Další oprava textu už do něj sama nevleze, musíte exportovat znovu. diff --git a/content/prezentacni-grafika/4-rocnik/07-export-a-formaty.en.md b/content/prezentacni-grafika/4-rocnik/07-export-a-formaty.en.md new file mode 100644 index 0000000..af63637 --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/07-export-a-formaty.en.md @@ -0,0 +1,6 @@ +--- +nazev: Export and formats +popis: What goes to the printer, to the web and out as video. +nazev_cviceni: Quiz: formats +nadpis_kvizu: Match the file to the job +--- diff --git a/content/prezentacni-grafika/4-rocnik/07-export-a-formaty.md b/content/prezentacni-grafika/4-rocnik/07-export-a-formaty.md new file mode 100644 index 0000000..d98d04a --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/07-export-a-formaty.md @@ -0,0 +1,79 @@ +--- +id: pg-4-export +nazev: Export a formáty +popis: Co se odevzdává tiskárně, na web a jako video. +nazev_cviceni: Kvíz: formáty +nadpis_kvizu: Přiřaďte soubor k použití +--- + +# Výklad + +## Tabulka výstupů +*Kam co* + +- Tiskárna dostane PDF. Plakát se spadávkou, leták A4 se spadávkou 3 mm. +- Web dostane JPG nebo PNG. Logo navíc SVG. +- Video dostane MP4, H.264, 1920 × 1080, 25 sn/s, 10 Mb/s. +- Pracovní soubory PSD, AI, AEP, PRPROJ a INDD se odevzdávají do složky zdroj, ne místo výstupů. + +> Tip: Jeden formát nespasí všechno. SVG na projektor s videem nehraje a MP4 tiskárna nevytiskne. + +## Jména +*Verze* + +- Název ať říká, co soubor je: plakat-tisk.pdf, plakat-web.jpg, skolni-den.mp4, logo.svg. +- Datum verze pište jako 2026-06-12, rok-měsíc-den. Seřadí se samo. +- Nepoužívejte final, final2 a oprava. Nová verze má nové datum, stará zůstane ve složce stare. +- Mezery a háčky v názvech na Windows i Macu projdou, ale odkaz mezi programy občas spadne. Bez háčků je jistější. + +> Tip: skolni-den_2026-06-12.mp4 je jasnější než video1.mp4. + +## Složka odevzdání +*Dvě hromádky* + +- Složka zdroj: ai, psd, aep, prproj, indd a použitá média. +- Složka export: pdf, jpg, png, svg, mp4. +- Média videa leží tam, kam vede odkaz projektu. Kopie na ploše se nepočítá. +- Když učitel otevře jen složku export, musí vidět plakát, leták, logo i video bez Adobe. + +> Tip: INDD bez odkazovaných fotek otevře prázdné rámečky. Fotky patří k němu, nebo se před odevzdáním zabalí. + +## Zadání: srovnat složku +*Cvičení* + +- Založte složku skolni-den se složkami zdroj a export. +- Do export dejte aspoň logo.svg, plakat-tisk.pdf, plakat-web.jpg a skolni-den.mp4. +- Do zdroj dejte odpovídající ai, psd, aep a prproj. +- Cizí soubor Bez názvu-1 smažte nebo přejmenujte. Ve složce ať není nic, co neumíte vysvětlit. + +> Tip: InDesign umí Soubor > Sbalit. Vznikne složka s INDD i s obrázky. Tu dejte do zdroj. + +# Kvíz + +## Co dostane tiskárna? +- prproj +- [x] PDF +- AEP +- Jen SVG na plakát A3 +> Tisk je PDF se spadávkou. Projekt Premiere tiskárna neotevře. + +## Který soubor loga patří na web a zůstane ostrý? +- logo.jpg o šířce 80 px +- [x] logo.svg +- skolni-den.mp4 +- plakat-tisk.pdf jako ikona +> SVG je křivka. Malé JPG se na webu rozmaže, jakmile ho stránka zvětší. + +## Jak se má jmenovat nová verze videa z 12. 6. 2026? +- final2.mp4 +- [x] skolni-den_2026-06-12.mp4 +- oprava oprava.mp4 +- DSC_1842.mp4 +> Datum rok-měsíc-den se seřadí a je vidět, která verze je nová. + +## Co musí být ve složce export, aby to otevřel člověk bez Adobe? +- Jen AEP a PRPROJ +- [x] PDF, JPG nebo PNG, SVG a MP4 +- Jen PSD +- Jen INDD +> Export je plochý výstup. Pracovní soubory Adobe bez programu nejdou přehrát ani otevřít naplno. diff --git a/content/prezentacni-grafika/4-rocnik/08-maturitni-zadani.en.md b/content/prezentacni-grafika/4-rocnik/08-maturitni-zadani.en.md new file mode 100644 index 0000000..93917ab --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/08-maturitni-zadani.en.md @@ -0,0 +1,6 @@ +--- +nazev: Exam assignment +popis: A Školní den campaign: a mark, a poster, a leaflet and a video. +nazev_cviceni: Quiz: the campaign +nadpis_kvizu: Walk through the whole hand-in +--- diff --git a/content/prezentacni-grafika/4-rocnik/08-maturitni-zadani.md b/content/prezentacni-grafika/4-rocnik/08-maturitni-zadani.md new file mode 100644 index 0000000..0dd6875 --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/08-maturitni-zadani.md @@ -0,0 +1,79 @@ +--- +id: pg-4-maturita +nazev: Maturitní zadání +popis: Kampaň Školní den: značka, plakát, leták a video. +nazev_cviceni: Kvíz: kampaň +nadpis_kvizu: Projdi celé odevzdání +--- + +# Výklad + +## Značka +*Illustrator* + +- Značka ze tří tvarů a názvu. Plná verze a černá verze, obě v logo.ai. +- Export logo.svg a logo.pdf. Černá verze jako logo-cerna.svg. +- Na 16 px musí být značka pořád čitelná. Když není, uberete detail. +- JPG není zdroj. Smí být jen náhled 1600 px. + +> Tip: Jedna značka na plakátu, v letáku i ve videu. Tři různé značky jsou tři práce, ne kampaň. + +## Tisk +*Plakát a leták* + +- Plakát A3 na výšku, plátno 303 × 426 mm, 300 ppi, spadávka 3 mm, text mimo ořez. +- Na plakátu je 12. 6. a tělocvična, fotka, maska, křivky a skupiny foto, grafika, text. +- Leták InDesign, A4, dvě strany, spadávka 3 mm, předloha s číslem strany, fotka jako odkaz. +- Výstupy: plakat-tisk.pdf se značkami, plakat-web.jpg 1600 px, letak.pdf. + +> Tip: Stejný název akce, stejné datum. Plakát a leták se nesmí rozejít. + +## Video +*Premiere a After Effects* + +- Úvod 8 sekund z After Effects: logo z 0 % na 100 % za 1 sekundu, text 1 + 4 + 1, snímky s F9. +- Úvod je v Premiere přes Dynamic Link, nebo jako jednou exportovaná kopie, když Link nejde. +- Celé video 40 až 60 sekund, titulky, jeden J-cut, hudba −15 a pod řečí −24 dB, řeč kolem −6 dB. +- Export skolni-den.mp4, H.264, 1920 × 1080, 25 sn/s, 10 Mb/s. + +> Tip: Na začátku mp4 hraje úvod, ne černá a ne ořezové značky z plakátu. + +## Složka +*Obhajoba* + +- skolni-den/zdroj drží ai, psd, aep, prproj, indd a média. +- skolni-den/export drží svg, pdf, jpg a mp4. Otevře je i člověk bez Adobe. +- Na obhajobu ukážete nejdřív video, pak plakát, pak jeden pracovní soubor s vrstvami. +- Řeknete, co je bitmapa a co vektor a proč je spadávka 3 mm. To je obsah čtyř ročníků. + +> Tip: Když se zeptají, proč není všechno v jednom JPG, odpověď je: tisk, web a video chtějí každý svůj soubor. + +# Kvíz + +## Co musí mít značka? +- [x] Tři tvary, plnou a černou verzi, SVG a PDF +- Jen JPG 80 px +- Jen titulek v Premiere +- Čtyři různé barvy na každém materiálu +> Zdroj je AI. SVG a PDF jsou výstupy. Černá verze je pro jednu barvu. + +## Jaké je plátno plakátu? +- A4 bez spadávky +- [x] 303 × 426 mm při 300 ppi +- 1920 × 1080 jako jediný tiskový rozměr +- 8 sekund +> A3 plus 3 mm z každé strany. 300 ppi. Video je zvlášť, ve Full HD. + +## Jak je dlouhý úvod z After Effects? +- [x] 8 sekund, logo nastoupí za 1 sekundu +- 45 sekund +- 12 snímků celé +- 4 minuty +> Kompozice má 8 sekund. Měřítko loga jde z 0 % na 100 % během první z nich. + +## Co leží ve složce export? +- Jen prproj a aep +- [x] SVG, PDF, JPG a MP4 +- Jen PSD s vrstvami +- Rozpracované bez názvu +> Export otevře i člověk bez Adobe. Pracovní soubory jsou ve složce zdroj. diff --git a/ios/Graduately/AppModel.swift b/ios/Graduately/AppModel.swift index 774dddd..5fad4f7 100644 --- a/ios/Graduately/AppModel.swift +++ b/ios/Graduately/AppModel.swift @@ -249,6 +249,26 @@ final class AppModel: ObservableObject { refresh() } + func markPg(_ id: Int) { + progress.markPg(id) + refresh() + } + + func markPg2(_ id: Int) { + progress.markPg2(id) + refresh() + } + + func markPg3(_ id: Int) { + progress.markPg3(id) + refresh() + } + + func markPg4(_ id: Int) { + progress.markPg4(id) + refresh() + } + func markTg4(_ id: Int) { progress.markTg4(id) refresh() diff --git a/ios/Graduately/Data/Content.swift b/ios/Graduately/Data/Content.swift index 4cb54aa..ca8da2a 100644 --- a/ios/Graduately/Data/Content.swift +++ b/ios/Graduately/Data/Content.swift @@ -63,6 +63,10 @@ final class Content { let tg2: [J] let tg3: [J] let tg4: [J] + let pg: [J] + let pg2: [J] + let pg3: [J] + let pg4: [J] let cs: [J] let cs2: [J] let cs3: [J] @@ -137,6 +141,10 @@ final class Content { tg2 = raw.arr("tg2") tg3 = raw.arr("tg3") tg4 = raw.arr("tg4") + pg = raw.arr("pg") + pg2 = raw.arr("pg2") + pg3 = raw.arr("pg3") + pg4 = raw.arr("pg4") cs = raw.arr("cs") cs2 = raw.arr("cs2") cs3 = raw.arr("cs3") @@ -207,6 +215,10 @@ final class Content { func tg2Lesson(_ id: Int) -> J? { tg2.first { $0.int("id") == id } } func tg3Lesson(_ id: Int) -> J? { tg3.first { $0.int("id") == id } } func tg4Lesson(_ id: Int) -> J? { tg4.first { $0.int("id") == id } } + func pgLesson(_ id: Int) -> J? { pg.first { $0.int("id") == id } } + func pg2Lesson(_ id: Int) -> J? { pg2.first { $0.int("id") == id } } + func pg3Lesson(_ id: Int) -> J? { pg3.first { $0.int("id") == id } } + func pg4Lesson(_ id: Int) -> J? { pg4.first { $0.int("id") == id } } func csLesson(_ id: Int) -> J? { cs.first { $0.int("id") == id } } func cs2Lesson(_ id: Int) -> J? { cs2.first { $0.int("id") == id } } func cs3Lesson(_ id: Int) -> J? { cs3.first { $0.int("id") == id } } diff --git a/ios/Graduately/Data/ProgressStore.swift b/ios/Graduately/Data/ProgressStore.swift index 50d8033..c9b8a64 100644 --- a/ios/Graduately/Data/ProgressStore.swift +++ b/ios/Graduately/Data/ProgressStore.swift @@ -102,6 +102,14 @@ final class ProgressStore { func markTg3(_ id: Int) { mark("tg3.\(id)") } func tg4Done(_ id: Int) -> Bool { done("tg4.\(id)") } func markTg4(_ id: Int) { mark("tg4.\(id)") } + func pgDone(_ id: Int) -> Bool { done("pg.\(id)") } + func markPg(_ id: Int) { mark("pg.\(id)") } + func pg2Done(_ id: Int) -> Bool { done("pg2.\(id)") } + func markPg2(_ id: Int) { mark("pg2.\(id)") } + func pg3Done(_ id: Int) -> Bool { done("pg3.\(id)") } + func markPg3(_ id: Int) { mark("pg3.\(id)") } + func pg4Done(_ id: Int) -> Bool { done("pg4.\(id)") } + func markPg4(_ id: Int) { mark("pg4.\(id)") } func csDone(_ id: Int) -> Bool { done("cs.\(id)") } func markCs(_ id: Int) { mark("cs.\(id)") } func cs2Done(_ id: Int) -> Bool { done("cs2.\(id)") } @@ -336,6 +344,38 @@ func summarize(_ content: Content, _ p: ProgressStore) -> ProgressSum { sum.doneUnits += 1 } } + for l in content.pg { + sum.totalEx += 1 + sum.openUnits += 1 + if p.pgDone(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.pg2 { + sum.totalEx += 1 + sum.openUnits += 1 + if p.pg2Done(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.pg3 { + sum.totalEx += 1 + sum.openUnits += 1 + if p.pg3Done(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.pg4 { + sum.totalEx += 1 + sum.openUnits += 1 + if p.pg4Done(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } for l in content.cs { sum.totalEx += 1 sum.openUnits += 1 diff --git a/ios/Graduately/Route.swift b/ios/Graduately/Route.swift index 3e3b2bc..005c538 100644 --- a/ios/Graduately/Route.swift +++ b/ios/Graduately/Route.swift @@ -124,6 +124,19 @@ enum Route: Hashable { case tg4Map case tg4Lesson(Int) case tg4Ex(Int) + case pgYears + case pgMap + case pgLesson(Int) + case pgEx(Int) + case pg2Map + case pg2Lesson(Int) + case pg2Ex(Int) + case pg3Map + case pg3Lesson(Int) + case pg3Ex(Int) + case pg4Map + case pg4Lesson(Int) + case pg4Ex(Int) case csYears case csMap case csLesson(Int) @@ -259,6 +272,19 @@ enum Route: Hashable { case .tg4Map: return "tg4map" case .tg4Lesson(let id): return "tg4unit\(id)" case .tg4Ex(let id): return "tg4ex\(id)" + case .pgYears: return "pgyears" + case .pgMap: return "pgmap" + case .pgLesson(let id): return "pgunit\(id)" + case .pgEx(let id): return "pgex\(id)" + case .pg2Map: return "pg2map" + case .pg2Lesson(let id): return "pg2unit\(id)" + case .pg2Ex(let id): return "pg2ex\(id)" + case .pg3Map: return "pg3map" + case .pg3Lesson(let id): return "pg3unit\(id)" + case .pg3Ex(let id): return "pg3ex\(id)" + case .pg4Map: return "pg4map" + case .pg4Lesson(let id): return "pg4unit\(id)" + case .pg4Ex(let id): return "pg4ex\(id)" case .csYears: return "csyears" case .csMap: return "csmap" case .csLesson(let id): return "csunit\(id)" @@ -518,6 +544,32 @@ enum Route: Hashable { return [.tgYears, .tg4Map, .tg4Lesson(id)] case .tg4Ex(let id): return [.tgYears, .tg4Map, .tg4Lesson(id), .tg4Ex(id)] + case .pgYears: + return [.pgYears] + case .pgMap: + return [.pgYears, .pgMap] + case .pgLesson(let id): + return [.pgYears, .pgMap, .pgLesson(id)] + case .pgEx(let id): + return [.pgYears, .pgMap, .pgLesson(id), .pgEx(id)] + case .pg2Map: + return [.pgYears, .pg2Map] + case .pg2Lesson(let id): + return [.pgYears, .pg2Map, .pg2Lesson(id)] + case .pg2Ex(let id): + return [.pgYears, .pg2Map, .pg2Lesson(id), .pg2Ex(id)] + case .pg3Map: + return [.pgYears, .pg3Map] + case .pg3Lesson(let id): + return [.pgYears, .pg3Map, .pg3Lesson(id)] + case .pg3Ex(let id): + return [.pgYears, .pg3Map, .pg3Lesson(id), .pg3Ex(id)] + case .pg4Map: + return [.pgYears, .pg4Map] + case .pg4Lesson(let id): + return [.pgYears, .pg4Map, .pg4Lesson(id)] + case .pg4Ex(let id): + return [.pgYears, .pg4Map, .pg4Lesson(id), .pg4Ex(id)] case .csYears: return [.csYears] case .csMap: @@ -601,6 +653,11 @@ func routeFromPage(_ page: String) -> Route? { case "tg2map": return .tg2Map case "tg3map": return .tg3Map case "tg4map": return .tg4Map + case "pgyears": return .pgYears + case "pgmap": return .pgMap + case "pg2map": return .pg2Map + case "pg3map": return .pg3Map + case "pg4map": return .pg4Map case "csyears": return .csYears case "csmap": return .csMap case "cs2map": return .cs2Map @@ -715,6 +772,30 @@ func routeFromPage(_ page: String) -> Route? { if let m = page.wholeMatch(of: /fyzex(\d+)/), let n = Int(m.1) { return .fyzEx(n) } + if let m = page.wholeMatch(of: /pg4unit(\d+)/), let n = Int(m.1) { + return .pg4Lesson(n) + } + if let m = page.wholeMatch(of: /pg4ex(\d+)/), let n = Int(m.1) { + return .pg4Ex(n) + } + if let m = page.wholeMatch(of: /pg3unit(\d+)/), let n = Int(m.1) { + return .pg3Lesson(n) + } + if let m = page.wholeMatch(of: /pg3ex(\d+)/), let n = Int(m.1) { + return .pg3Ex(n) + } + if let m = page.wholeMatch(of: /pg2unit(\d+)/), let n = Int(m.1) { + return .pg2Lesson(n) + } + if let m = page.wholeMatch(of: /pg2ex(\d+)/), let n = Int(m.1) { + return .pg2Ex(n) + } + if let m = page.wholeMatch(of: /pgunit(\d+)/), let n = Int(m.1) { + return .pgLesson(n) + } + if let m = page.wholeMatch(of: /pgex(\d+)/), let n = Int(m.1) { + return .pgEx(n) + } if let m = page.wholeMatch(of: /tg4unit(\d+)/), let n = Int(m.1) { return .tg4Lesson(n) } diff --git a/ios/Graduately/UI/Components.swift b/ios/Graduately/UI/Components.swift index ec5bd56..d93afdc 100644 --- a/ios/Graduately/UI/Components.swift +++ b/ios/Graduately/UI/Components.swift @@ -711,6 +711,7 @@ func subjectSymbol(_ icon: String) -> String { case "function": return "function" case "ruler": return "ruler.fill" case "code": return "chevron.left.forwardslash.chevron.right" + case "brush": return "paintbrush.fill" default: return "lock.fill" } } diff --git a/ios/Graduately/UI/ExercisesMore.swift b/ios/Graduately/UI/ExercisesMore.swift index 53a8dc1..3ae847b 100644 --- a/ios/Graduately/UI/ExercisesMore.swift +++ b/ios/Graduately/UI/ExercisesMore.swift @@ -985,6 +985,39 @@ struct TgQuizScreen: View { } } +struct PgQuizScreen: View { + @ObservedObject var vm: AppModel + let year: Int + let id: Int + + var body: some View { + let lesson: J? = { + switch year { + case 2: return vm.content.pg2Lesson(id) + case 3: return vm.content.pg3Lesson(id) + case 4: return vm.content.pg4Lesson(id) + default: return vm.content.pgLesson(id) + } + }() + if let lesson { + let spec = J([ + "type": "choice", + "title": vm.tr(lesson.str("exTitleKey")), + "sub": lesson.str("quizHeadKey"), + "questions": lesson.rawArray("quiz"), + ]) + DispatchExercise(vm: vm, spec: spec, titleFallback: vm.tr(lesson.str("exTitleKey"))) { + switch year { + case 2: vm.markPg2(id) + case 3: vm.markPg3(id) + case 4: vm.markPg4(id) + default: vm.markPg(id) + } + } + } + } +} + struct FyzQuizScreen: View { @ObservedObject var vm: AppModel let year: Int diff --git a/ios/Graduately/UI/Screens.swift b/ios/Graduately/UI/Screens.swift index 02230cf..54a6a28 100644 --- a/ios/Graduately/UI/Screens.swift +++ b/ios/Graduately/UI/Screens.swift @@ -747,6 +747,10 @@ func routeProgress(_ vm: AppModel, _ r: Route) -> (done: Int, total: Int)? { case .tg2Map: return tally(c.tg2, p.tg2Done) case .tg3Map: return tally(c.tg3, p.tg3Done) case .tg4Map: return tally(c.tg4, p.tg4Done) + case .pgMap: return tally(c.pg, p.pgDone) + case .pg2Map: return tally(c.pg2, p.pg2Done) + case .pg3Map: return tally(c.pg3, p.pg3Done) + case .pg4Map: return tally(c.pg4, p.pg4Done) case .csMap: return tally(c.cs, p.csDone) case .cs2Map: return tally(c.cs2, p.cs2Done) case .cs3Map: return tally(c.cs3, p.cs3Done) @@ -810,6 +814,12 @@ private func subjectSum(_ vm: AppModel, _ s: J) -> ProgressSum { + vm.content.cs2.filter { vm.progress.cs2Done($0.int("id")) }.count + vm.content.cs3.filter { vm.progress.cs3Done($0.int("id")) }.count + vm.content.cs4.filter { vm.progress.cs4Done($0.int("id")) }.count + case "pgyears", "pgmap", "pg2map", "pg3map", "pg4map": + sum.totalEx = vm.content.pg.count + vm.content.pg2.count + vm.content.pg3.count + vm.content.pg4.count + sum.doneEx = vm.content.pg.filter { vm.progress.pgDone($0.int("id")) }.count + + vm.content.pg2.filter { vm.progress.pg2Done($0.int("id")) }.count + + vm.content.pg3.filter { vm.progress.pg3Done($0.int("id")) }.count + + vm.content.pg4.filter { vm.progress.pg4Done($0.int("id")) }.count case "tgyears", "tgmap", "tg2map", "tg3map", "tg4map": sum.totalEx = vm.content.tg.count + vm.content.tg2.count + vm.content.tg3.count + vm.content.tg4.count sum.doneEx = vm.content.tg.filter { vm.progress.tgDone($0.int("id")) }.count @@ -1119,6 +1129,27 @@ private func buildSearch(_ vm: AppModel) -> [Hit] { add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "fyz4unit\(l.int("id"))", extra: "fyzika") add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "fyz4ex\(l.int("id"))") } + add(vm.tr("Prezentační grafika"), vm.tr("search_lesson"), "pgyears", extra: "photoshop premiere illustrator") + add(vm.tr("pg_year1"), vm.tr("search_lesson"), "pgmap", extra: "rocnik") + add(vm.tr("pg_year2"), vm.tr("search_lesson"), "pg2map", extra: "rocnik") + add(vm.tr("pg_year3"), vm.tr("search_lesson"), "pg3map", extra: "rocnik") + add(vm.tr("pg_year4"), vm.tr("search_lesson"), "pg4map", extra: "rocnik") + for l in vm.content.pg { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "pgunit\(l.int("id"))", extra: "photoshop") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "pgex\(l.int("id"))") + } + for l in vm.content.pg2 { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "pg2unit\(l.int("id"))", extra: "photoshop") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "pg2ex\(l.int("id"))") + } + for l in vm.content.pg3 { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "pg3unit\(l.int("id"))", extra: "premiere") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "pg3ex\(l.int("id"))") + } + for l in vm.content.pg4 { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "pg4unit\(l.int("id"))", extra: "illustrator") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "pg4ex\(l.int("id"))") + } add(vm.tr("Technická grafika"), vm.tr("search_lesson"), "tgyears", extra: "autocad grafika") add(vm.tr("tg_year1"), vm.tr("search_lesson"), "tgmap", extra: "rocnik") add(vm.tr("tg_year2"), vm.tr("search_lesson"), "tg2map", extra: "rocnik") @@ -1945,6 +1976,47 @@ struct CsYearsScreen: View { } } +struct PgYearsScreen: View { + @ObservedObject var vm: AppModel + + var body: some View { + #if os(macOS) + let years: [(String, String, Route)] = [ + ("pg_year1", "pg_sub", .pgMap), + ("pg_year2", "pg2_sub", .pg2Map), + ("pg_year3", "pg3_sub", .pg3Map), + ("pg_year4", "pg4_sub", .pg4Map), + ] + MacFillGrid(count: years.count, minWidth: 260, minHeight: 200) { i in + NavigationLink(value: years[i].2) { + MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).", progress: routeProgress(vm, years[i].2)) + } + .buttonStyle(PressScaleStyle()) + } + .navigationTitle(vm.tr("Prezentační grafika")) + .navigationSubtitle(vm.tr("pg_years_sub")) + #else + List { + NavigationLink(value: Route.pgMap) { + ListRowLabel(title: vm.tr("pg_year1"), subtitle: vm.tr("pg_sub"), progress: routeProgress(vm, Route.pgMap)) + } + NavigationLink(value: Route.pg2Map) { + ListRowLabel(title: vm.tr("pg_year2"), subtitle: vm.tr("pg2_sub"), progress: routeProgress(vm, Route.pg2Map)) + } + NavigationLink(value: Route.pg3Map) { + ListRowLabel(title: vm.tr("pg_year3"), subtitle: vm.tr("pg3_sub"), progress: routeProgress(vm, Route.pg3Map)) + } + NavigationLink(value: Route.pg4Map) { + ListRowLabel(title: vm.tr("pg_year4"), subtitle: vm.tr("pg4_sub"), progress: routeProgress(vm, Route.pg4Map)) + } + } + .navigationTitle(vm.tr("Prezentační grafika")) + .navigationSubtitle(vm.tr("pg_years_sub")) + .appListStyle() + #endif + } +} + struct TgYearsScreen: View { @ObservedObject var vm: AppModel @@ -3103,6 +3175,72 @@ struct RouteDestination: View { if let l = vm.content.cs4Lesson(id) { CodeLessonScreen(vm: vm, lesson: l, onSuccess: { vm.markCs4(id) }) } + case .pgYears: + PgYearsScreen(vm: vm) + case .pgMap: + LessonListScreen( + vm: vm, + title: vm.tr("pg_year1"), + subtitle: vm.tr("pg_sub"), + rows: vm.content.pg.map { l in + let id = l.int("id") + return LessonRow(id: "p\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.pgDone(id), destination: .pgLesson(id)) + } + ) + case .pgLesson(let id): + if let l = vm.content.pgLesson(id) { + SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .pgEx(id)) + } + case .pgEx(let id): + PgQuizScreen(vm: vm, year: 1, id: id) + case .pg2Map: + LessonListScreen( + vm: vm, + title: vm.tr("pg_year2"), + subtitle: vm.tr("pg2_sub"), + rows: vm.content.pg2.map { l in + let id = l.int("id") + return LessonRow(id: "p2\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.pg2Done(id), destination: .pg2Lesson(id)) + } + ) + case .pg2Lesson(let id): + if let l = vm.content.pg2Lesson(id) { + SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .pg2Ex(id)) + } + case .pg2Ex(let id): + PgQuizScreen(vm: vm, year: 2, id: id) + case .pg3Map: + LessonListScreen( + vm: vm, + title: vm.tr("pg_year3"), + subtitle: vm.tr("pg3_sub"), + rows: vm.content.pg3.map { l in + let id = l.int("id") + return LessonRow(id: "p3\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.pg3Done(id), destination: .pg3Lesson(id)) + } + ) + case .pg3Lesson(let id): + if let l = vm.content.pg3Lesson(id) { + SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .pg3Ex(id)) + } + case .pg3Ex(let id): + PgQuizScreen(vm: vm, year: 3, id: id) + case .pg4Map: + LessonListScreen( + vm: vm, + title: vm.tr("pg_year4"), + subtitle: vm.tr("pg4_sub"), + rows: vm.content.pg4.map { l in + let id = l.int("id") + return LessonRow(id: "p4\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.pg4Done(id), destination: .pg4Lesson(id)) + } + ) + case .pg4Lesson(let id): + if let l = vm.content.pg4Lesson(id) { + SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .pg4Ex(id)) + } + case .pg4Ex(let id): + PgQuizScreen(vm: vm, year: 4, id: id) case .tgYears: TgYearsScreen(vm: vm) case .tgMap: diff --git a/scripts/build-content.py b/scripts/build-content.py index efcd131..12bf473 100644 --- a/scripts/build-content.py +++ b/scripts/build-content.py @@ -1108,6 +1108,8 @@ STD = [ ("fyzika/3-rocnik", "fyz3"), ("fyzika/4-rocnik", "fyz4"), ("technicka-grafika/1-rocnik", "tg"), ("technicka-grafika/2-rocnik", "tg2"), ("technicka-grafika/3-rocnik", "tg3"), ("technicka-grafika/4-rocnik", "tg4"), + ("prezentacni-grafika/1-rocnik", "pg"), ("prezentacni-grafika/2-rocnik", "pg2"), + ("prezentacni-grafika/3-rocnik", "pg3"), ("prezentacni-grafika/4-rocnik", "pg4"), ] CODE = [ ("programovani/1-rocnik", "cs"), ("programovani/2-rocnik", "cs2"), From 264f4a73d87632e0b31ffe25f69d1bfc9a243c95 Mon Sep 17 00:00:00 2001 From: pepaskopec-blip Date: Sun, 11 Oct 2026 19:25:48 +0200 Subject: [PATCH 6/7] Open software as four years from Windows and Office to policy. Co-authored-by: Cursor --- README.md | 4 +- .../shared/src/org/maturita/maturita/App.kt | 59 ++++++++ .../src/org/maturita/maturita/AppViewModel.kt | 20 +++ .../shared/src/org/maturita/maturita/Route.kt | 55 +++++++ .../src/org/maturita/maturita/data/Content.kt | 8 + .../maturita/maturita/data/ProgressStore.kt | 40 +++++ .../src/org/maturita/maturita/ui/Exercises.kt | 24 +++ .../src/org/maturita/maturita/ui/Screens.kt | 48 ++++++ content/README.md | 4 +- content/aplikace/novinky.txt | 6 + content/aplikace/predmety.md | 2 +- content/aplikace/texty.md | 14 ++ .../1-rocnik/01-software-a-licence.en.md | 6 + .../1-rocnik/01-software-a-licence.md | 79 ++++++++++ .../1-rocnik/02-operacni-system.en.md | 6 + .../1-rocnik/02-operacni-system.md | 79 ++++++++++ .../1-rocnik/03-soubory-a-slozky.en.md | 6 + .../1-rocnik/03-soubory-a-slozky.md | 79 ++++++++++ .../1-rocnik/04-instalace-a-aktualizace.en.md | 6 + .../1-rocnik/04-instalace-a-aktualizace.md | 79 ++++++++++ .../1-rocnik/05-ucty.en.md | 6 + .../programove-vybaveni/1-rocnik/05-ucty.md | 79 ++++++++++ .../1-rocnik/06-spravce-uloh.en.md | 6 + .../1-rocnik/06-spravce-uloh.md | 79 ++++++++++ .../1-rocnik/07-zabezpeceni.en.md | 6 + .../1-rocnik/07-zabezpeceni.md | 79 ++++++++++ .../1-rocnik/08-zadani-windows.en.md | 6 + .../1-rocnik/08-zadani-windows.md | 79 ++++++++++ .../2-rocnik/01-word-styly.en.md | 6 + .../2-rocnik/01-word-styly.md | 79 ++++++++++ .../2-rocnik/02-word-tabulka-a-obsah.en.md | 6 + .../2-rocnik/02-word-tabulka-a-obsah.md | 79 ++++++++++ .../2-rocnik/03-word-dopis.en.md | 6 + .../2-rocnik/03-word-dopis.md | 79 ++++++++++ .../2-rocnik/04-excel-bunky.en.md | 6 + .../2-rocnik/04-excel-bunky.md | 79 ++++++++++ .../2-rocnik/05-excel-funkce.en.md | 6 + .../2-rocnik/05-excel-funkce.md | 79 ++++++++++ .../2-rocnik/06-excel-grafy.en.md | 6 + .../2-rocnik/06-excel-grafy.md | 79 ++++++++++ .../2-rocnik/07-excel-kontingence.en.md | 6 + .../2-rocnik/07-excel-kontingence.md | 79 ++++++++++ .../2-rocnik/08-zadani-office.en.md | 6 + .../2-rocnik/08-zadani-office.md | 79 ++++++++++ .../3-rocnik/01-databaze.en.md | 6 + .../3-rocnik/01-databaze.md | 79 ++++++++++ .../3-rocnik/02-access-tabulky.en.md | 6 + .../3-rocnik/02-access-tabulky.md | 79 ++++++++++ .../3-rocnik/03-access-dotazy.en.md | 6 + .../3-rocnik/03-access-dotazy.md | 79 ++++++++++ .../3-rocnik/04-access-formulare.en.md | 6 + .../3-rocnik/04-access-formulare.md | 79 ++++++++++ .../3-rocnik/05-mysql-select.en.md | 6 + .../3-rocnik/05-mysql-select.md | 79 ++++++++++ .../3-rocnik/06-mysql-zmeny.en.md | 6 + .../3-rocnik/06-mysql-zmeny.md | 79 ++++++++++ .../3-rocnik/07-mysql-join.en.md | 6 + .../3-rocnik/07-mysql-join.md | 79 ++++++++++ .../3-rocnik/08-zadani-databaze.en.md | 6 + .../3-rocnik/08-zadani-databaze.md | 79 ++++++++++ .../4-rocnik/01-wordpress.en.md | 6 + .../4-rocnik/01-wordpress.md | 79 ++++++++++ .../4-rocnik/02-wordpress-role.en.md | 6 + .../4-rocnik/02-wordpress-role.md | 79 ++++++++++ .../4-rocnik/03-power-bi.en.md | 6 + .../4-rocnik/03-power-bi.md | 79 ++++++++++ .../4-rocnik/04-power-bi-prurez.en.md | 6 + .../4-rocnik/04-power-bi-prurez.md | 79 ++++++++++ .../4-rocnik/05-batch.en.md | 6 + .../programove-vybaveni/4-rocnik/05-batch.md | 79 ++++++++++ .../4-rocnik/06-uzivatele-a-skupiny.en.md | 6 + .../4-rocnik/06-uzivatele-a-skupiny.md | 79 ++++++++++ .../4-rocnik/07-zasady.en.md | 6 + .../programove-vybaveni/4-rocnik/07-zasady.md | 79 ++++++++++ .../4-rocnik/08-maturitni-zadani.en.md | 6 + .../4-rocnik/08-maturitni-zadani.md | 79 ++++++++++ ios/Graduately/AppModel.swift | 20 +++ ios/Graduately/Data/Content.swift | 12 ++ ios/Graduately/Data/ProgressStore.swift | 40 +++++ ios/Graduately/Route.swift | 81 ++++++++++ ios/Graduately/UI/Components.swift | 1 + ios/Graduately/UI/ExercisesMore.swift | 33 +++++ ios/Graduately/UI/Screens.swift | 138 ++++++++++++++++++ scripts/build-content.py | 2 + 84 files changed, 3327 insertions(+), 4 deletions(-) create mode 100644 content/programove-vybaveni/1-rocnik/01-software-a-licence.en.md create mode 100644 content/programove-vybaveni/1-rocnik/01-software-a-licence.md create mode 100644 content/programove-vybaveni/1-rocnik/02-operacni-system.en.md create mode 100644 content/programove-vybaveni/1-rocnik/02-operacni-system.md create mode 100644 content/programove-vybaveni/1-rocnik/03-soubory-a-slozky.en.md create mode 100644 content/programove-vybaveni/1-rocnik/03-soubory-a-slozky.md create mode 100644 content/programove-vybaveni/1-rocnik/04-instalace-a-aktualizace.en.md create mode 100644 content/programove-vybaveni/1-rocnik/04-instalace-a-aktualizace.md create mode 100644 content/programove-vybaveni/1-rocnik/05-ucty.en.md create mode 100644 content/programove-vybaveni/1-rocnik/05-ucty.md create mode 100644 content/programove-vybaveni/1-rocnik/06-spravce-uloh.en.md create mode 100644 content/programove-vybaveni/1-rocnik/06-spravce-uloh.md create mode 100644 content/programove-vybaveni/1-rocnik/07-zabezpeceni.en.md create mode 100644 content/programove-vybaveni/1-rocnik/07-zabezpeceni.md create mode 100644 content/programove-vybaveni/1-rocnik/08-zadani-windows.en.md create mode 100644 content/programove-vybaveni/1-rocnik/08-zadani-windows.md create mode 100644 content/programove-vybaveni/2-rocnik/01-word-styly.en.md create mode 100644 content/programove-vybaveni/2-rocnik/01-word-styly.md create mode 100644 content/programove-vybaveni/2-rocnik/02-word-tabulka-a-obsah.en.md create mode 100644 content/programove-vybaveni/2-rocnik/02-word-tabulka-a-obsah.md create mode 100644 content/programove-vybaveni/2-rocnik/03-word-dopis.en.md create mode 100644 content/programove-vybaveni/2-rocnik/03-word-dopis.md create mode 100644 content/programove-vybaveni/2-rocnik/04-excel-bunky.en.md create mode 100644 content/programove-vybaveni/2-rocnik/04-excel-bunky.md create mode 100644 content/programove-vybaveni/2-rocnik/05-excel-funkce.en.md create mode 100644 content/programove-vybaveni/2-rocnik/05-excel-funkce.md create mode 100644 content/programove-vybaveni/2-rocnik/06-excel-grafy.en.md create mode 100644 content/programove-vybaveni/2-rocnik/06-excel-grafy.md create mode 100644 content/programove-vybaveni/2-rocnik/07-excel-kontingence.en.md create mode 100644 content/programove-vybaveni/2-rocnik/07-excel-kontingence.md create mode 100644 content/programove-vybaveni/2-rocnik/08-zadani-office.en.md create mode 100644 content/programove-vybaveni/2-rocnik/08-zadani-office.md create mode 100644 content/programove-vybaveni/3-rocnik/01-databaze.en.md create mode 100644 content/programove-vybaveni/3-rocnik/01-databaze.md create mode 100644 content/programove-vybaveni/3-rocnik/02-access-tabulky.en.md create mode 100644 content/programove-vybaveni/3-rocnik/02-access-tabulky.md create mode 100644 content/programove-vybaveni/3-rocnik/03-access-dotazy.en.md create mode 100644 content/programove-vybaveni/3-rocnik/03-access-dotazy.md create mode 100644 content/programove-vybaveni/3-rocnik/04-access-formulare.en.md create mode 100644 content/programove-vybaveni/3-rocnik/04-access-formulare.md create mode 100644 content/programove-vybaveni/3-rocnik/05-mysql-select.en.md create mode 100644 content/programove-vybaveni/3-rocnik/05-mysql-select.md create mode 100644 content/programove-vybaveni/3-rocnik/06-mysql-zmeny.en.md create mode 100644 content/programove-vybaveni/3-rocnik/06-mysql-zmeny.md create mode 100644 content/programove-vybaveni/3-rocnik/07-mysql-join.en.md create mode 100644 content/programove-vybaveni/3-rocnik/07-mysql-join.md create mode 100644 content/programove-vybaveni/3-rocnik/08-zadani-databaze.en.md create mode 100644 content/programove-vybaveni/3-rocnik/08-zadani-databaze.md create mode 100644 content/programove-vybaveni/4-rocnik/01-wordpress.en.md create mode 100644 content/programove-vybaveni/4-rocnik/01-wordpress.md create mode 100644 content/programove-vybaveni/4-rocnik/02-wordpress-role.en.md create mode 100644 content/programove-vybaveni/4-rocnik/02-wordpress-role.md create mode 100644 content/programove-vybaveni/4-rocnik/03-power-bi.en.md create mode 100644 content/programove-vybaveni/4-rocnik/03-power-bi.md create mode 100644 content/programove-vybaveni/4-rocnik/04-power-bi-prurez.en.md create mode 100644 content/programove-vybaveni/4-rocnik/04-power-bi-prurez.md create mode 100644 content/programove-vybaveni/4-rocnik/05-batch.en.md create mode 100644 content/programove-vybaveni/4-rocnik/05-batch.md create mode 100644 content/programove-vybaveni/4-rocnik/06-uzivatele-a-skupiny.en.md create mode 100644 content/programove-vybaveni/4-rocnik/06-uzivatele-a-skupiny.md create mode 100644 content/programove-vybaveni/4-rocnik/07-zasady.en.md create mode 100644 content/programove-vybaveni/4-rocnik/07-zasady.md create mode 100644 content/programove-vybaveni/4-rocnik/08-maturitni-zadani.en.md create mode 100644 content/programove-vybaveni/4-rocnik/08-maturitni-zadani.md diff --git a/README.md b/README.md index 9ab13ae..8605cce 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,8 @@ Intel Mac, jiná architektura nebo úpravy kódu → [sestavení ze zdroje](#ses - **Prezentační grafika** — 4 ročníky, v každém 8 lekcí: Photoshop od pixelu po plakát, Premiere Pro a ve čtvrtém ročníku Illustrator, After Effects a InDesign +- **Programové vybavení** — 4 ročníky, v každém 8 lekcí: Windows a licence, + Word a Excel, Access a MySQL, pak WordPress, Power BI, dávkové soubory a zásady - **Programování** — 4 ročníky C# po vzoru W3Schools. U každé lekce se kód píše a spouští vedle výkladu, od výpisu po třídy, LINQ a async - **Český jazyk a literatura** — literatura (4 ročníky, 32 lekcí podle osnov @@ -97,7 +99,7 @@ Intel Mac, jiná architektura nebo úpravy kódu → [sestavení ze zdroje](#ses ### Použití -1. **Úvod** ukáže postup a mřížku předmětů. Otevřený předmět spustí cestu. Zamčená dlaždice zatím nic neotevře: programové vybavení. +1. **Úvod** ukáže postup a mřížku předmětů. Každý předmět spustí svou cestu. 2. U němčiny je učebnice (Neue Freunde, Aus aller Welt, Bei uns zu Hause) a čtyři ročníky. Ostatní otevřené předměty mají ročníky, lekce nebo knihy. 3. Z cesty se otevře výklad, poslech, čtení, psaní nebo kvíz. 4. **Statistiky**, **Nastavení** a **Hledat** jsou v postranním panelu na Macu. Na telefonu a v desktopové aplikaci jsou na záložkách, hledání navíc ikonou lupy. Zkratka je `Cmd/Ctrl+K`. diff --git a/android/shared/src/org/maturita/maturita/App.kt b/android/shared/src/org/maturita/maturita/App.kt index a814af3..27eef9d 100644 --- a/android/shared/src/org/maturita/maturita/App.kt +++ b/android/shared/src/org/maturita/maturita/App.kt @@ -34,6 +34,8 @@ import org.maturita.maturita.ui.LessonListScreen import org.maturita.maturita.ui.FyzQuizScreen import org.maturita.maturita.ui.PgQuizScreen import org.maturita.maturita.ui.PgYearsScreen +import org.maturita.maturita.ui.SwQuizScreen +import org.maturita.maturita.ui.SwYearsScreen import org.maturita.maturita.ui.TgQuizScreen import org.maturita.maturita.ui.TgYearsScreen import org.maturita.maturita.ui.MathPracticeScreen @@ -645,6 +647,63 @@ private fun MaturitaAppBody(vm: AppViewModel) { } } is Route.Pg4Ex -> PgQuizScreen(vm, 4, r.id) + Route.SwYears -> SwYearsScreen(vm) + Route.SwMap -> LessonListScreen( + vm, vm.tr("sw_year1"), vm.tr("sw_sub"), + vm.content.sw.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.swDone(id), Route.SwLesson(id)) + }, + ) + is Route.SwLesson -> { + val l = vm.content.swLesson(r.id) + if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) { + vm.replace(Route.SwEx(r.id)) + } + } + is Route.SwEx -> SwQuizScreen(vm, 1, r.id) + Route.Sw2Map -> LessonListScreen( + vm, vm.tr("sw_year2"), vm.tr("sw2_sub"), + vm.content.sw2.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.sw2Done(id), Route.Sw2Lesson(id)) + }, + ) + is Route.Sw2Lesson -> { + val l = vm.content.sw2Lesson(r.id) + if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) { + vm.replace(Route.Sw2Ex(r.id)) + } + } + is Route.Sw2Ex -> SwQuizScreen(vm, 2, r.id) + Route.Sw3Map -> LessonListScreen( + vm, vm.tr("sw_year3"), vm.tr("sw3_sub"), + vm.content.sw3.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.sw3Done(id), Route.Sw3Lesson(id)) + }, + ) + is Route.Sw3Lesson -> { + val l = vm.content.sw3Lesson(r.id) + if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) { + vm.replace(Route.Sw3Ex(r.id)) + } + } + is Route.Sw3Ex -> SwQuizScreen(vm, 3, r.id) + Route.Sw4Map -> LessonListScreen( + vm, vm.tr("sw_year4"), vm.tr("sw4_sub"), + vm.content.sw4.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.sw4Done(id), Route.Sw4Lesson(id)) + }, + ) + is Route.Sw4Lesson -> { + val l = vm.content.sw4Lesson(r.id) + if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) { + vm.replace(Route.Sw4Ex(r.id)) + } + } + is Route.Sw4Ex -> SwQuizScreen(vm, 4, r.id) Route.CsYears -> CsYearsScreen(vm) Route.CsMap -> LessonListScreen( vm, vm.tr("cs_year1"), vm.tr("cs_sub"), diff --git a/android/shared/src/org/maturita/maturita/AppViewModel.kt b/android/shared/src/org/maturita/maturita/AppViewModel.kt index 2eeca6a..4be6a80 100644 --- a/android/shared/src/org/maturita/maturita/AppViewModel.kt +++ b/android/shared/src/org/maturita/maturita/AppViewModel.kt @@ -282,6 +282,26 @@ class AppViewModel(val content: Content, val platform: Platform) { refresh() } + fun markSw(id: Int) { + progress.markSw(id) + refresh() + } + + fun markSw2(id: Int) { + progress.markSw2(id) + refresh() + } + + fun markSw3(id: Int) { + progress.markSw3(id) + refresh() + } + + fun markSw4(id: Int) { + progress.markSw4(id) + refresh() + } + fun markCs(id: Int) { progress.markCs(id) refresh() diff --git a/android/shared/src/org/maturita/maturita/Route.kt b/android/shared/src/org/maturita/maturita/Route.kt index 705f15d..af79af8 100644 --- a/android/shared/src/org/maturita/maturita/Route.kt +++ b/android/shared/src/org/maturita/maturita/Route.kt @@ -131,6 +131,19 @@ sealed class Route(val page: String) { data object Pg4Map : Route("pg4map") data class Pg4Lesson(val id: Int) : Route("pg4unit$id") data class Pg4Ex(val id: Int) : Route("pg4ex$id") + data object SwYears : Route("swyears") + data object SwMap : Route("swmap") + data class SwLesson(val id: Int) : Route("swunit$id") + data class SwEx(val id: Int) : Route("swex$id") + data object Sw2Map : Route("sw2map") + data class Sw2Lesson(val id: Int) : Route("sw2unit$id") + data class Sw2Ex(val id: Int) : Route("sw2ex$id") + data object Sw3Map : Route("sw3map") + data class Sw3Lesson(val id: Int) : Route("sw3unit$id") + data class Sw3Ex(val id: Int) : Route("sw3ex$id") + data object Sw4Map : Route("sw4map") + data class Sw4Lesson(val id: Int) : Route("sw4unit$id") + data class Sw4Ex(val id: Int) : Route("sw4ex$id") data object CsYears : Route("csyears") data object CsMap : Route("csmap") data class CsLesson(val id: Int) : Route("csunit$id") @@ -298,6 +311,19 @@ sealed class Route(val page: String) { Pg4Map -> listOf(Subjects, PgYears, Pg4Map) is Pg4Lesson -> listOf(Subjects, PgYears, Pg4Map, this) is Pg4Ex -> listOf(Subjects, PgYears, Pg4Map, Pg4Lesson(id), this) + SwYears -> listOf(Subjects, SwYears) + SwMap -> listOf(Subjects, SwYears, SwMap) + is SwLesson -> listOf(Subjects, SwYears, SwMap, this) + is SwEx -> listOf(Subjects, SwYears, SwMap, SwLesson(id), this) + Sw2Map -> listOf(Subjects, SwYears, Sw2Map) + is Sw2Lesson -> listOf(Subjects, SwYears, Sw2Map, this) + is Sw2Ex -> listOf(Subjects, SwYears, Sw2Map, Sw2Lesson(id), this) + Sw3Map -> listOf(Subjects, SwYears, Sw3Map) + is Sw3Lesson -> listOf(Subjects, SwYears, Sw3Map, this) + is Sw3Ex -> listOf(Subjects, SwYears, Sw3Map, Sw3Lesson(id), this) + Sw4Map -> listOf(Subjects, SwYears, Sw4Map) + is Sw4Lesson -> listOf(Subjects, SwYears, Sw4Map, this) + is Sw4Ex -> listOf(Subjects, SwYears, Sw4Map, Sw4Lesson(id), this) CsYears -> listOf(Subjects, CsYears) CsMap -> listOf(Subjects, CsYears, CsMap) is CsLesson -> listOf(Subjects, CsYears, CsMap, this) @@ -369,6 +395,11 @@ fun routeFromPage(page: String): Route? = when (page) { "pg2map" -> Route.Pg2Map "pg3map" -> Route.Pg3Map "pg4map" -> Route.Pg4Map + "swyears" -> Route.SwYears + "swmap" -> Route.SwMap + "sw2map" -> Route.Sw2Map + "sw3map" -> Route.Sw3Map + "sw4map" -> Route.Sw4Map "csyears" -> Route.CsYears "csmap" -> Route.CsMap "cs2map" -> Route.Cs2Map @@ -519,6 +550,30 @@ fun routeFromPage(page: String): Route? = when (page) { Regex("""pgex(\d+)""").matchEntire(page)?.let { return Route.PgEx(it.groupValues[1].toInt()) } + Regex("""sw4unit(\d+)""").matchEntire(page)?.let { + return Route.Sw4Lesson(it.groupValues[1].toInt()) + } + Regex("""sw4ex(\d+)""").matchEntire(page)?.let { + return Route.Sw4Ex(it.groupValues[1].toInt()) + } + Regex("""sw3unit(\d+)""").matchEntire(page)?.let { + return Route.Sw3Lesson(it.groupValues[1].toInt()) + } + Regex("""sw3ex(\d+)""").matchEntire(page)?.let { + return Route.Sw3Ex(it.groupValues[1].toInt()) + } + Regex("""sw2unit(\d+)""").matchEntire(page)?.let { + return Route.Sw2Lesson(it.groupValues[1].toInt()) + } + Regex("""sw2ex(\d+)""").matchEntire(page)?.let { + return Route.Sw2Ex(it.groupValues[1].toInt()) + } + Regex("""swunit(\d+)""").matchEntire(page)?.let { + return Route.SwLesson(it.groupValues[1].toInt()) + } + Regex("""swex(\d+)""").matchEntire(page)?.let { + return Route.SwEx(it.groupValues[1].toInt()) + } Regex("""cs4unit(\d+)""").matchEntire(page)?.let { return Route.Cs4Lesson(it.groupValues[1].toInt()) } diff --git a/android/shared/src/org/maturita/maturita/data/Content.kt b/android/shared/src/org/maturita/maturita/data/Content.kt index 802a9a3..ecb0c93 100644 --- a/android/shared/src/org/maturita/maturita/data/Content.kt +++ b/android/shared/src/org/maturita/maturita/data/Content.kt @@ -64,6 +64,10 @@ class Content(root: JSONObject) { val pg2 = raw.arr("pg2") val pg3 = raw.arr("pg3") val pg4 = raw.arr("pg4") + val sw = raw.arr("sw") + val sw2 = raw.arr("sw2") + val sw3 = raw.arr("sw3") + val sw4 = raw.arr("sw4") val cs = raw.arr("cs") val cs2 = raw.arr("cs2") val cs3 = raw.arr("cs3") @@ -128,6 +132,10 @@ class Content(root: JSONObject) { fun pg2Lesson(id: Int) = pg2.firstOrNull { it.int("id") == id } fun pg3Lesson(id: Int) = pg3.firstOrNull { it.int("id") == id } fun pg4Lesson(id: Int) = pg4.firstOrNull { it.int("id") == id } + fun swLesson(id: Int) = sw.firstOrNull { it.int("id") == id } + fun sw2Lesson(id: Int) = sw2.firstOrNull { it.int("id") == id } + fun sw3Lesson(id: Int) = sw3.firstOrNull { it.int("id") == id } + fun sw4Lesson(id: Int) = sw4.firstOrNull { it.int("id") == id } fun csLesson(id: Int) = cs.firstOrNull { it.int("id") == id } fun cs2Lesson(id: Int) = cs2.firstOrNull { it.int("id") == id } fun cs3Lesson(id: Int) = cs3.firstOrNull { it.int("id") == id } diff --git a/android/shared/src/org/maturita/maturita/data/ProgressStore.kt b/android/shared/src/org/maturita/maturita/data/ProgressStore.kt index 7dd417c..f4e5607 100644 --- a/android/shared/src/org/maturita/maturita/data/ProgressStore.kt +++ b/android/shared/src/org/maturita/maturita/data/ProgressStore.kt @@ -107,6 +107,14 @@ class ProgressStore(private val prefs: Prefs, content: Content) { fun markPg3(id: Int) { mark("pg3.$id") } fun pg4Done(id: Int) = done("pg4.$id") fun markPg4(id: Int) { mark("pg4.$id") } + fun swDone(id: Int) = done("sw.$id") + fun markSw(id: Int) { mark("sw.$id") } + fun sw2Done(id: Int) = done("sw2.$id") + fun markSw2(id: Int) { mark("sw2.$id") } + fun sw3Done(id: Int) = done("sw3.$id") + fun markSw3(id: Int) { mark("sw3.$id") } + fun sw4Done(id: Int) = done("sw4.$id") + fun markSw4(id: Int) { mark("sw4.$id") } fun csDone(id: Int) = done("cs.$id") fun markCs(id: Int) { mark("cs.$id") } fun cs2Done(id: Int) = done("cs2.$id") @@ -368,6 +376,38 @@ fun summarize(content: Content, p: ProgressStore): ProgressSum { sum.doneUnits += 1 } } + content.sw.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.swDone(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.sw2.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.sw2Done(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.sw3.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.sw3Done(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.sw4.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.sw4Done(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } content.cs.forEach { l -> sum.totalEx += 1 sum.openUnits += 1 diff --git a/android/shared/src/org/maturita/maturita/ui/Exercises.kt b/android/shared/src/org/maturita/maturita/ui/Exercises.kt index 8343011..4bf3a47 100644 --- a/android/shared/src/org/maturita/maturita/ui/Exercises.kt +++ b/android/shared/src/org/maturita/maturita/ui/Exercises.kt @@ -1130,6 +1130,30 @@ fun PgQuizScreen(vm: AppViewModel, year: Int, id: Int) { } } +@Composable +fun SwQuizScreen(vm: AppViewModel, year: Int, id: Int) { + val lesson = when (year) { + 2 -> vm.content.sw2Lesson(id) + 3 -> vm.content.sw3Lesson(id) + 4 -> vm.content.sw4Lesson(id) + else -> vm.content.swLesson(id) + } ?: return + val spec = J(org.json.JSONObject().apply { + put("type", "choice") + put("title", vm.tr(lesson.str("exTitleKey"))) + put("sub", lesson.str("quizHeadKey")) + put("questions", lesson.o.optJSONArray("quiz")) + }) + DispatchExercise(vm, spec, vm.tr(lesson.str("exTitleKey"))) { + when (year) { + 2 -> vm.markSw2(id) + 3 -> vm.markSw3(id) + 4 -> vm.markSw4(id) + else -> vm.markSw(id) + } + } +} + @Composable fun FyzQuizScreen(vm: AppViewModel, year: Int, id: Int) { val lesson = when (year) { diff --git a/android/shared/src/org/maturita/maturita/ui/Screens.kt b/android/shared/src/org/maturita/maturita/ui/Screens.kt index afd4624..1f57231 100644 --- a/android/shared/src/org/maturita/maturita/ui/Screens.kt +++ b/android/shared/src/org/maturita/maturita/ui/Screens.kt @@ -25,6 +25,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.MenuBook +import androidx.compose.material.icons.filled.Apps import androidx.compose.material.icons.filled.Architecture import androidx.compose.material.icons.filled.AutoAwesome import androidx.compose.material.icons.filled.AutoStories @@ -248,6 +249,7 @@ private fun SubjectIcon(icon: String, open: Boolean) { "ruler" -> Icon(Icons.Filled.Architecture, null, tint = tint, modifier = Modifier.size(26.dp)) "code" -> Icon(Icons.Filled.Terminal, null, tint = tint, modifier = Modifier.size(26.dp)) "brush" -> Icon(Icons.Filled.Brush, null, tint = tint, modifier = Modifier.size(26.dp)) + "apps" -> Icon(Icons.Filled.Apps, null, tint = tint, modifier = Modifier.size(26.dp)) else -> Icon(Icons.Filled.Lock, null, tint = tint) } } @@ -369,6 +371,10 @@ fun routeProgress(vm: AppViewModel, r: Route): Pair? { Route.Pg2Map -> tally(c.pg2, p::pg2Done) Route.Pg3Map -> tally(c.pg3, p::pg3Done) Route.Pg4Map -> tally(c.pg4, p::pg4Done) + Route.SwMap -> tally(c.sw, p::swDone) + Route.Sw2Map -> tally(c.sw2, p::sw2Done) + Route.Sw3Map -> tally(c.sw3, p::sw3Done) + Route.Sw4Map -> tally(c.sw4, p::sw4Done) Route.CsMap -> tally(c.cs, p::csDone) Route.Cs2Map -> tally(c.cs2, p::cs2Done) Route.Cs3Map -> tally(c.cs3, p::cs3Done) @@ -457,6 +463,13 @@ private fun subjectSum(vm: AppViewModel, s: J): ProgressSum { vm.content.pg3.count { vm.progress.pg3Done(it.int("id")) } + vm.content.pg4.count { vm.progress.pg4Done(it.int("id")) } } + "swyears", "swmap", "sw2map", "sw3map", "sw4map" -> { + sum.totalEx = vm.content.sw.size + vm.content.sw2.size + vm.content.sw3.size + vm.content.sw4.size + sum.doneEx = vm.content.sw.count { vm.progress.swDone(it.int("id")) } + + vm.content.sw2.count { vm.progress.sw2Done(it.int("id")) } + + vm.content.sw3.count { vm.progress.sw3Done(it.int("id")) } + + vm.content.sw4.count { vm.progress.sw4Done(it.int("id")) } + } "czechmap" -> { val litDone = vm.content.lit.count { vm.progress.litDone(it.int("id")) } + vm.content.lit2.count { vm.progress.lit2Done(it.int("id")) } + @@ -758,6 +771,27 @@ private fun buildSearch(vm: AppViewModel): List { add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "fyz4unit${l.int("id")}", extra = "fyzika") add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "fyz4ex${l.int("id")}") } + add(vm.tr("Programové vybavení"), vm.tr("search_lesson"), "swyears", extra = "office windows excel mysql") + add(vm.tr("sw_year1"), vm.tr("search_lesson"), "swmap", extra = "rocnik") + add(vm.tr("sw_year2"), vm.tr("search_lesson"), "sw2map", extra = "rocnik") + add(vm.tr("sw_year3"), vm.tr("search_lesson"), "sw3map", extra = "rocnik") + add(vm.tr("sw_year4"), vm.tr("search_lesson"), "sw4map", extra = "rocnik") + vm.content.sw.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "swunit${l.int("id")}", extra = "windows") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "swex${l.int("id")}") + } + vm.content.sw2.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "sw2unit${l.int("id")}", extra = "word excel") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "sw2ex${l.int("id")}") + } + vm.content.sw3.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "sw3unit${l.int("id")}", extra = "access mysql") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "sw3ex${l.int("id")}") + } + vm.content.sw4.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "sw4unit${l.int("id")}", extra = "wordpress") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "sw4ex${l.int("id")}") + } add(vm.tr("Prezentační grafika"), vm.tr("search_lesson"), "pgyears", extra = "photoshop premiere illustrator") add(vm.tr("pg_year1"), vm.tr("search_lesson"), "pgmap", extra = "rocnik") add(vm.tr("pg_year2"), vm.tr("search_lesson"), "pg2map", extra = "rocnik") @@ -1032,6 +1066,20 @@ fun CsYearsScreen(vm: AppViewModel) { } } +@Composable +fun SwYearsScreen(vm: AppViewModel) { + DetailScaffold(vm, vm.tr("Programové vybavení"), vm.tr("sw_years_sub")) { inner -> + LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { + item { + NavRow(title = vm.tr("sw_year1"), subtitle = vm.tr("sw_sub"), leading = { LeadingBadge("1") }, progress = routeProgress(vm, Route.SwMap)) { vm.go(Route.SwMap) } + NavRow(title = vm.tr("sw_year2"), subtitle = vm.tr("sw2_sub"), leading = { LeadingBadge("2") }, progress = routeProgress(vm, Route.Sw2Map)) { vm.go(Route.Sw2Map) } + NavRow(title = vm.tr("sw_year3"), subtitle = vm.tr("sw3_sub"), leading = { LeadingBadge("3") }, progress = routeProgress(vm, Route.Sw3Map)) { vm.go(Route.Sw3Map) } + NavRow(title = vm.tr("sw_year4"), subtitle = vm.tr("sw4_sub"), leading = { LeadingBadge("4") }, progress = routeProgress(vm, Route.Sw4Map)) { vm.go(Route.Sw4Map) } + } + } + } +} + @Composable fun PgYearsScreen(vm: AppViewModel) { DetailScaffold(vm, vm.tr("Prezentační grafika"), vm.tr("pg_years_sub")) { inner -> diff --git a/content/README.md b/content/README.md index dfa12a4..ed6dca9 100644 --- a/content/README.md +++ b/content/README.md @@ -18,7 +18,7 @@ Skript vypíše počet lekcí, nebo chyby s cestou a číslem řádku | Složka | Obsah | |---|---| -| `pocitacove-site/`, `technicke-vybaveni/`, `technicka-grafika/`, `prezentacni-grafika/`, `programovani/`, `obcanska-nauka/`, `fyzika/` | `N-rocnik/`, v nich lekce | +| `pocitacove-site/`, `technicke-vybaveni/`, `technicka-grafika/`, `prezentacni-grafika/`, `programove-vybaveni/`, `programovani/`, `obcanska-nauka/`, `fyzika/` | `N-rocnik/`, v nich lekce | | `prirodni-vedy/chemie`, `prirodni-vedy/biologie` | lekce | | `cestina/literatura/N-rocnik` | lekce literatury | | `cestina/mluvnice` | cvičení z mluvnice | @@ -97,7 +97,7 @@ a smažte `puvodni`. ## Běžná lekce -Sítě, hardware, technická grafika, prezentační grafika, občanka, literatura, chemie, biologie, fyzika. +Sítě, hardware, technická grafika, prezentační grafika, programové vybavení, občanka, literatura, chemie, biologie, fyzika. ```md --- diff --git a/content/aplikace/novinky.txt b/content/aplikace/novinky.txt index 280d4c3..e8365ba 100644 --- a/content/aplikace/novinky.txt +++ b/content/aplikace/novinky.txt @@ -5,6 +5,12 @@ # cs: bullet # en: bullet +--- 2026-10-11 +cs: Programové vybavení: Windows, Word, Excel, Access, MySQL a správa účtů +cs: Čtyři ročníky, od licence a složek po WordPress, Power BI a zásady +en: Software: Windows, Word, Excel, Access, MySQL and account administration +en: Four years, from licences and folders to WordPress, Power BI and policy + --- 2026-10-11 cs: Prezentační grafika: Photoshop, Premiere Pro a další programy Adobe cs: Čtyři ročníky, v každé lekci postup, zadání a kvíz diff --git a/content/aplikace/predmety.md b/content/aplikace/predmety.md index d55c8ec..37363dd 100644 --- a/content/aplikace/predmety.md +++ b/content/aplikace/predmety.md @@ -14,4 +14,4 @@ | Technická grafika | Technická grafika | Technical Graphics | ruler | tgyears | ano | | Prezentační grafika | Prezentační grafika | Presentation Graphics | brush | pgyears | ano | | Programování | Programování | Programming | code | csyears | ano | -| Programové vybavení | Programové vybavení | Software | lock | | ne | +| Programové vybavení | Programové vybavení | Software | apps | swyears | ano | diff --git a/content/aplikace/texty.md b/content/aplikace/texty.md index 5177ae8..fb62a94 100644 --- a/content/aplikace/texty.md +++ b/content/aplikace/texty.md @@ -258,6 +258,20 @@ | pg3_sub | Premiere Pro: střih, zvuk, titulky a video. | Premiere Pro: the cut, sound, titles and the video. | | pg4_sub | Illustrator, After Effects, InDesign a maturitní kampaň. | Illustrator, After Effects, InDesign and the exam campaign. | +# Programové vybavení + +| Klíč | Česky | Anglicky | +|---|---|---| +| sw_years_sub | Operační systém, Office, databáze a správa Windows. | The operating system, Office, databases and Windows administration. | +| sw_year1 | 1. ročník | Year 1 | +| sw_year2 | 2. ročník | Year 2 | +| sw_year3 | 3. ročník | Year 3 | +| sw_year4 | 4. ročník | Year 4 | +| sw_sub | Software, licence a práce ve Windows. | Software, licences and working in Windows. | +| sw2_sub | Word a Excel: dokument, vzorce a graf. | Word and Excel: a document, formulas and a chart. | +| sw3_sub | Access a MySQL: tabulky, dotazy a vztahy. | Access and MySQL: tables, queries and relationships. | +| sw4_sub | WordPress, Power BI, dávkové soubory a zásady. | WordPress, Power BI, batch files and policy. | + # Programování | Klíč | Česky | Anglicky | diff --git a/content/programove-vybaveni/1-rocnik/01-software-a-licence.en.md b/content/programove-vybaveni/1-rocnik/01-software-a-licence.en.md new file mode 100644 index 0000000..77b2182 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/01-software-a-licence.en.md @@ -0,0 +1,6 @@ +--- +nazev: Software and licences +popis: System and application programs, and the terms for using them. +nazev_cviceni: Quiz: software and licences +nadpis_kvizu: Name the kind of program and the kind of licence +--- diff --git a/content/programove-vybaveni/1-rocnik/01-software-a-licence.md b/content/programove-vybaveni/1-rocnik/01-software-a-licence.md new file mode 100644 index 0000000..e40dee4 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/01-software-a-licence.md @@ -0,0 +1,79 @@ +--- +id: sw-1-software-a-licence +nazev: Software a licence +popis: Systémové a aplikační programy a to, za jakých podmínek se smí používat. +nazev_cviceni: Kvíz: software a licence +nadpis_kvizu: Poznejte druh programu a druh licence +--- + +# Výklad + +## Dva druhy programů +*Software* + +- Software je program. Hardware je zařízení. Samotný počítač bez programu nic neudělá. +- Operační systém řídí počítač: Windows, Linux, macOS. Ve škole pracujeme ve Windows. +- Aplikační software řeší jednu práci: Word dokument, Excel tabulku, prohlížeč web. +- Ovladač je malý program k zařízení. Tiskárna bez ovladače netiskne, i když je zapnutá. + +> Tip: Když program spadne a systém běží dál, spadla aplikace. Když nesvítí plocha, spadl systém. + +## Licence +*Podmínky* + +- Licence říká, kdo program smí nainstalovat a na kolik počítačů. +- Komerční program se kupuje, nebo se platí předplatné. Microsoft 365 je předplatné. +- OEM licence je přivázaná k jednomu počítači. Na nový stroj se nepřetahuje. +- Stažený program bez licence není zdarma. Je to kopie, kterou škola nesmí používat. + +> Tip: Školní účet Microsoft 365 nepatří na domácí počítače navždy. Platí, dokud škola předplatné má. + +## Zdarma není jeden pojem +*Freeware a open source* + +- Freeware je zdarma, ale zdrojový kód nedostanete. Smíte ho používat za podmínek autora. +- Open source má otevřený kód. GPL navíc chce, aby odvozené programy zůstaly otevřené. +- Zkušební verze (shareware) po čase skončí, nebo omezí funkce. Není to trvalá licence. +- Na školní počítač patří jen programy, které škola schválila. Vlastní instalace se hlásí učiteli. + +> Tip: Cena nula neznamená, že smíte program rozdávat dál. I freeware má licenci. + +## Zadání: šest programů +*Cvičení* + +- Do tabulky zapište šest programů ze školního počítače: systém, prohlížeč, Word, Excel a dva další. +- U každého napište, jestli je systémový, nebo aplikační. +- U Wordu a Excelu napište, jaká licence ve škole je: předplatné Microsoft 365, ne domácí krabice. +- Soubor pojmenujte licence.txt a uložte ho do Dokumenty. Zatím nic neinstalujte. + +> Tip: Název systému zjistíte příkazem winver. Okno ukáže edici Windows a verzi. + +# Kvíz + +## Kam patří Microsoft Word? +- Mezi operační systémy +- [x] Mezi aplikační software +- Mezi ovladače tiskárny +- Mezi hardware +> Word řeší jednu práci, dokument. Systém pod ním je Windows. + +## Co znamená OEM licence Windows? +- Smí se přenést na každý další počítač +- [x] Je vázaná na jeden počítač +- Je to totéž co GPL +- Platí jen 14 dní +> OEM se prodává s počítačem a na jiný stroj se nepřesouvá. + +## Čím se freeware liší od open source? +- Freeware má otevřený zdrojový kód +- [x] Freeware je zdarma, ale kód obvykle nedostanete +- Open source se nesmí používat ve škole +- Je to totéž +> Zdarma a otevřený kód jsou dvě různé věci. GPL je otevřená licence, ne jen nulová cena. + +## Který program je operační systém? +- Excel +- [x] Windows +- WordPress +- Power BI +> Windows řídí počítač. Ostatní jsou aplikace, které na systému běží. diff --git a/content/programove-vybaveni/1-rocnik/02-operacni-system.en.md b/content/programove-vybaveni/1-rocnik/02-operacni-system.en.md new file mode 100644 index 0000000..572309c --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/02-operacni-system.en.md @@ -0,0 +1,6 @@ +--- +nazev: The operating system +popis: What the system does between the user and the hardware. +nazev_cviceni: Quiz: the operating system +nadpis_kvizu: Name the role of the system +--- diff --git a/content/programove-vybaveni/1-rocnik/02-operacni-system.md b/content/programove-vybaveni/1-rocnik/02-operacni-system.md new file mode 100644 index 0000000..352a114 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/02-operacni-system.md @@ -0,0 +1,79 @@ +--- +id: sw-1-operacni-system +nazev: Operační systém +popis: Co systém dělá mezi uživatelem a hardwarem. +nazev_cviceni: Kvíz: operační systém +nadpis_kvizu: Poznejte roli systému +--- + +# Výklad + +## Mezi člověkem a strojem +*Role* + +- Systém spouští programy, hlídá paměť a přiděluje čas procesoru. +- Souborový systém ukládá data na disk. Ve Windows je to NTFS. +- Ovladač si systém načte, aby mluvil s tiskárnou, diskem a sítí. +- Uživatel vidí plochu a okna. Jádro pod tím nevidí a do něj nesahá. + +> Tip: Když dva programy chtějí tiskárnu najednou, rozhodne systém. Samy se nedomluví. + +## Tři systémy +*Windows, Linux, macOS* + +- Školní učebna je Windows. Příkazy a obrazovky v tomto předmětu jsou z Windows. +- Linux je častý na serverech. Stejný dokument v Excelu tam sám od sebe není. +- macOS běží na počítačích Apple. Zkratka Ctrl je tam často Cmd. +- Soubor .docx je dokument Wordu. Není to program a není to systém. + +> Tip: Přípona souboru říká, čím ho otevřít. Systém podle ní vybere aplikaci. + +## Start počítače +*Až po přihlášení* + +- Po zapnutí naběhne firmware, pak systém, pak přihlašovací obrazovka. +- Dokud se nepřihlásíte, pracujete s účtem nikoho. Soubory uživatele jsou zamčené. +- Každý účet má vlastní plochu a složku C:\Users\jméno. +- Vypnutí přes nabídku Start uloží práci systému. Dlouhý stisk tlačítka je až poslední možnost. + +> Tip: Restart po aktualizaci není trest. Systém si domlouvá soubory, které za běhu nevymění. + +## Zadání: jaký systém to je +*Cvičení* + +- Spusťte winver a opište edici a verzi, třeba Windows 11 Pro, verzi z okna. +- Otevřete C:\Users a napište, které složky uživatelů tam jsou. Do cizích nesaháte. +- Najděte jeden ovladač v názvu: ve Správci zařízení rozbalte Monitory nebo Tiskárny. +- Zápis uložte jako system.txt do Dokumenty. Hesla ani sériová čísla nepište. + +> Tip: Správce zařízení je ve Windows devmgmt.msc. Žlutý vykřičník znamená chybějící nebo vadný ovladač. + +# Kvíz + +## Co dělá operační systém s pamětí? +- Nic, paměť si berou programy samy +- [x] Přiděluje ji programům +- Maže ji při každém uložení dokumentu +- Patří do Excelu +> Program požádá, systém přidělí. Dva programy nesmí psát do stejného místa. + +## Jaký souborový systém používá školní Windows na disku? +- [x] NTFS +- Jen názvy souborů bez systému +- SVG +- MySQL +> NTFS drží soubory, oprávnění i větší disky. Názvy složek jsou až uvnitř něj. + +## Kdy smíte sáhnout do složky jiného uživatele v C:\Users? +- Kdykoli, je to jeden počítač +- [x] Nesmíte, je to jeho účet +- Jen když je složka na ploše +- Když má dokument příponu docx +> Každý účet má vlastní soubory. Cizí složka není společný šuplík. + +## Čím zjistíte edici Windows? +- [x] Příkazem winver +- Otevřením Excelu +- Smazáním koše +- Souborem .bat +> winver otevře okno O systému Windows s edicí a verzí. diff --git a/content/programove-vybaveni/1-rocnik/03-soubory-a-slozky.en.md b/content/programove-vybaveni/1-rocnik/03-soubory-a-slozky.en.md new file mode 100644 index 0000000..be64b98 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/03-soubory-a-slozky.en.md @@ -0,0 +1,6 @@ +--- +nazev: Files and folders +popis: A path, an extension and order in Documents. +nazev_cviceni: Quiz: files +nadpis_kvizu: Build the path and the name +--- diff --git a/content/programove-vybaveni/1-rocnik/03-soubory-a-slozky.md b/content/programove-vybaveni/1-rocnik/03-soubory-a-slozky.md new file mode 100644 index 0000000..8989f5a --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/03-soubory-a-slozky.md @@ -0,0 +1,79 @@ +--- +id: sw-1-soubory +nazev: Soubory a složky +popis: Cesta, přípona a pořádek v Dokumentech. +nazev_cviceni: Kvíz: soubory +nadpis_kvizu: Sestavte cestu a název +--- + +# Výklad + +## Cesta +*Kde soubor leží* + +- Cesta začíná diskem. C:\Users\novak\Documents\Skola\1-rocnik je složka, ne soubor. +- Zpětné lomítko odděluje složky. Dopředné lomítko je na webu, ne ve Windows. +- Dokumenty jsou %USERPROFILE%\Documents. Na každém účtu to vede jinam, příkaz je stejný. +- Plocha je taky složka. Soubor jen na ploše se ve školním pořádku ztratí. + +> Tip: Do řádku adresy v Průzkumníku vložte cestu a Enter. Nemusíte proklikávat pět složek. + +## Přípona +*Čím se soubor otevře* + +- prihlaska.docx je Word. znamky.xlsx je Excel. poznamka.txt je prostý text. +- Windows přípony schovává. Zapněte Zobrazit, Zobrazit, Přípony názvů souborů. +- Přejmenovat foto.jpg na foto.docx z něj dokument neudělá. Změní se jen štítek. +- Dvojitá přípona faktura.pdf.exe je program, který se tváří jako dokument. Neotevírejte ho. + +> Tip: Když přípony vidíte, podvodný soubor poznáte dřív, než na něj kliknete. + +## Názvy +*Co je zakázané* + +- V názvu nesmí být znaky \ / : * ? " < > |. +- Mezera smí být, ale v dávkových souborech pak název patří do uvozovek. +- Školní název je věcný: 2026-06-12-zadost.docx. Final-final2.docx není název. +- Datum pište rok-měsíc-den. Složka se seřadí od nejstaršího sama. + +> Tip: Kopírování je Ctrl+C, vložení Ctrl+V, přesun začíná Ctrl+X. Přesun z flashky na disk soubor na flashce smaže. + +## Zadání: strom školy +*Cvičení* + +- V Dokumentech založte Skola, v ní 1-rocnik a v ní slozky word, excel a system. +- Do word uložte prázdný soubor poznamka.txt. Příponu ať je vidět. +- Cesta ať končí na \Skola\1-rocnik\word\poznamka.txt. +- Na ploše nic nenechávejte. Koš nevysypávejte, dokud nevíte, co v něm je. + +> Tip: Nová složka je Ctrl+Shift+N. Když se jmenuje Nová složka, hned ji přejmenujte. + +# Kvíz + +## Která cesta vede k souboru v Dokumentech? +- [x] C:\Users\novak\Documents\Skola\1-rocnik\poznamka.txt +- https://skola/poznamka.txt +- poznamka.txt bez složky, to stačí vždy +- C:/Users/novak s obyčejnými lomítky jako jediná správná forma +> Ve Windows se složky oddělují zpětným lomítkem a cesta začíná diskem. + +## Co se stane, když foto.jpg přejmenujete na foto.docx? +- Stane se z něj upravitelný dokument Word +- [x] Změní se přípona, obsah zůstane obrázek +- Soubor se uloží do koše +- Vznikne tabulka Excel +> Přípona je nápověda pro systém. Bajty obrázku se samy nepřepíšou na dokument. + +## Který název je na školní odevzdání nejlepší? +- final2.docx +- [x] 2026-06-12-zadost.docx +- Nový dokument.docx +- ???.docx +> Datum rok-měsíc-den se seřadí a je z něj poznat verze. Otazník v názvu Windows ani nepustí. + +## Proč zapnout zobrazení přípon? +- Aby šly soubory mazat +- [x] Aby byl vidět skutečný typ, třeba pdf.exe +- Protože bez přípony soubor nejde uložit +- Excel to vyžaduje u každé buňky +> Skrytá přípona schová, že faktura.pdf.exe je program. diff --git a/content/programove-vybaveni/1-rocnik/04-instalace-a-aktualizace.en.md b/content/programove-vybaveni/1-rocnik/04-instalace-a-aktualizace.en.md new file mode 100644 index 0000000..2408177 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/04-instalace-a-aktualizace.en.md @@ -0,0 +1,6 @@ +--- +nazev: Installation and updates +popis: Where a program comes from and why the system is updated. +nazev_cviceni: Quiz: installation +nadpis_kvizu: Pick a safe source +--- diff --git a/content/programove-vybaveni/1-rocnik/04-instalace-a-aktualizace.md b/content/programove-vybaveni/1-rocnik/04-instalace-a-aktualizace.md new file mode 100644 index 0000000..4b787c9 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/04-instalace-a-aktualizace.md @@ -0,0 +1,79 @@ +--- +id: sw-1-instalace +nazev: Instalace a aktualizace +popis: Odkud se program bere a proč se systém aktualizuje. +nazev_cviceni: Kvíz: instalace +nadpis_kvizu: Vyberte bezpečný zdroj +--- + +# Výklad + +## Odkud instalovat +*Zdroj* + +- Školní programy berte z Microsoft Store, z balíčku školy, nebo z webu výrobce. +- Odkaz v mailu, tlačítko Stáhnout zdarma na cizím webu a crack nejsou zdroj. +- Instalace chce souhlas správce. Okno Řízení uživatelských účtů se neodklepává poslepu. +- Na plochu nepatří deset instalačních souborů. Po instalaci je smažte, program už je v systému. + +> Tip: Název souboru setup.exe nic neříká. Říká to vydavatel v okně účtů a adresa, odkud soubor přišel. + +## Aktualizace +*Windows Update* + +- Aktualizace zašívá chyby, přes které se do počítače leze. Odkládat ji týdny je riziko. +- Windows Update je v Nastavení, Windows Update. Školní počítač nechte doběhnout. +- Restart po aktualizaci udělejte, když o něj systém požádá. Ne uprostřed testu, ale ten den. +- Word a Excel se v Microsoft 365 aktualizují se sadou Office, ne jen s Windows. + +> Tip: Verze systému z winver se po velké aktualizaci změní. Když se měsíc nemění, aktualizace stojí. + +## Odinstalace +*Aplikace* + +- Program se odebírá v Nastavení, Aplikace, Nainstalované aplikace. Ne smazáním zástupce. +- Zástupce na ploše je jen odkaz. Smazáním ikony program v počítači zůstane. +- Neznámý program, který jste neinstalovali, nejdřív nahlaste. Nemusí to být hra spolužáka. +- Odinstalace nesmaže vaše dokumenty. Smaže program. Dokumenty si před velkým úklidem zkopírujte. + +> Tip: Když v seznamu aplikací Word není a zástupce ano, zástupce vede do prázdna, nebo na jiný disk. + +## Zadání: stav počítače +*Cvičení* + +- Z winver opište verzi. V Windows Update napište, jestli je systém aktuální, nebo něco čeká. +- V nainstalovaných aplikacích najděte Word nebo Microsoft 365 a opište název, jak ho seznam uvádí. +- Najděte jeden program, který nepoznáváte, a zapište ho. Neodinstalovávejte ho bez učitele. +- Uložte stav.txt do Skola\1-rocnik\system. Nic z internetu nestahujte. + +> Tip: Čas poslední aktualizace je ve Windows Update dole v historii. Stačí datum, ne seznam všech záplat. + +# Kvíz + +## Odkud se má školní program instalovat? +- Z odkazu v neznámém mailu +- [x] Z webu výrobce, z Store, nebo z balíčku školy +- Z stránky s nápisem crack +- Z přílohy faktura.pdf.exe +> Výrobce, obchod a škola jsou zdroje. Crack a náhodný mail nejsou. + +## Co udělá smazání zástupce na ploše? +- Odinstaluje program +- [x] Smaže jen odkaz, program zůstane +- Smaže Dokumenty +- Vypne aktualizace +> Zástupce je zkratka. Program se odebírá v Nainstalovaných aplikacích. + +## Proč se Windows Update nenechává měsíc stát? +- Protože jinak nejde otevřít Word +- [x] Záplaty zavírají známé díry +- Protože se jinak smaže plocha +- Aktualizace jen mění tapetu +> Neaktualizovaný systém má chyby, které už jsou veřejně popsané. + +## Kdy odklepnete okno Řízení uživatelských účtů? +- Vždycky, ať instalace pokračuje +- [x] Jen když víte, jaký program a odkud ho spouštíte +- Nikdy, ani u školního balíčku +- Jen v Excelu +> Okno říká, že program chce právo správce. Neznámý vydavatel se neodklepává. diff --git a/content/programove-vybaveni/1-rocnik/05-ucty.en.md b/content/programove-vybaveni/1-rocnik/05-ucty.en.md new file mode 100644 index 0000000..b525df1 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/05-ucty.en.md @@ -0,0 +1,6 @@ +--- +nazev: User accounts +popis: A standard user, an administrator and a password that is not the account name. +nazev_cviceni: Quiz: accounts +nadpis_kvizu: Pick the account type and the password +--- diff --git a/content/programove-vybaveni/1-rocnik/05-ucty.md b/content/programove-vybaveni/1-rocnik/05-ucty.md new file mode 100644 index 0000000..fc41b33 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/05-ucty.md @@ -0,0 +1,79 @@ +--- +id: sw-1-ucty +nazev: Uživatelské účty +popis: Běžný uživatel, správce a heslo, které není jméno účtu. +nazev_cviceni: Kvíz: účty +nadpis_kvizu: Vyberte typ účtu a heslo +--- + +# Výklad + +## Dva typy +*Správce a uživatel* + +- Správce smí instalovat programy a měnit systém. Ve škole je to účet učitele nebo správce učebny. +- Běžný uživatel smí pracovat se svými soubory. Systémové složky mu systém zamkne. +- Denní práce nepatří na účet správce. Virus by dostal stejná práva jako vy. +- Nový školní účet pro cvičení se jmenuje zaci a je běžný uživatel, ne správce. + +> Tip: Když systém požádá o heslo správce, běžný účet na to nestačí. To je záměr, ne porucha. + +## Heslo +*Osm znaků* + +- Heslo má aspoň 8 znaků a není to jméno účtu, ani slovo heslo, ani 12345678. +- Do příkazu se heslo nepíše. Příkaz net user zaci * /add se na heslo zeptá a na obrazovce ho neukáže. +- Heslo na papírek na monitor nepatří. Patří do hlavy, nebo do školního sejfu hesel, když ho učitel určí. +- Cizí heslo se nezkouší. Ani když je účet spolužáka odhlášený a počítač je společný. + +> Tip: Hvězdička v net user znamená dotaz na heslo. Heslo napsané za příkazem zůstane v historii příkazů. + +## Vlastní soubory +*Profil* + +- Profil je složka C:\Users\zaci. Jsou v ní Dokumenty, Plocha a Stažené soubory. +- Soubor uložený do Dokumentů tohoto účtu jiný běžný uživatel neotevře. +- Společná složka C:\Users\Public je pro věci, které mají vidět všichni. Úkoly tam nedávejte. +- Odhlášení účet zavře. Vypnutí počítače taky. Přepnutí uživatele nechá první účet otevřený v paměti. + +> Tip: Na sdíleném počítači se odhlašujte. Přepnutí uživatele nechá otevřené dokumenty v paměti. + +## Zadání: účet na papíře +*Cvičení* + +- Napište, jak by se založil běžný účet zaci: příkaz net user zaci * /add spuštěný jako správce. +- Napište heslo, které pravidlům nevyhovuje, a druhé, které má aspoň 8 znaků a není jméno účtu. Skutečné školní heslo nepište. +- Účet nesmí být ve skupině Administrators. Patří mezi běžné Users. +- Uložte ucet.txt do Skola\1-rocnik\system. Účet v systému zakládejte jen když to učitel výslovně řekne. + +> Tip: Kdo jsem, zjistí příkaz whoami. Když vypíše správce, na denní práci jste na špatném účtu. + +# Kvíz + +## Jaký má být cvičný účet zaci? +- Správce, ať jde všechno +- [x] Běžný uživatel +- Účet bez hesla +- Účet hosta sdílený celou třídou bez hesla +> Denní práce běží jako běžný uživatel. Správce je na instalaci a správu, ne na Word. + +## Jak se heslo předá příkazu net user? +- Napíše se za příkaz, ať je v sešitě vidět +- [x] Hvězdičkou, příkaz se zeptá a heslo neukáže +- Do názvu účtu +- Do souboru na plochu +> net user zaci * /add se zeptá. Heslo v příkazu zůstane v historii. + +## Proč se denně nepracuje jako správce? +- Word jinak nejde otevřít +- [x] Program, který spustíte, dostane právo měnit systém +- Správce nemá Dokumenty +- Excel jinak nepočítá +> Škodlivý program dědí vaše práva. U správce smí instalovat a měnit systém. + +## Kam patří osobní úkol? +- Do C:\Users\Public +- [x] Do Dokumentů vlastního účtu +- Do složky Windows +- Na účet spolužáka +> Public vidí každý účet na počítači. Úkol patří do vlastního profilu. diff --git a/content/programove-vybaveni/1-rocnik/06-spravce-uloh.en.md b/content/programove-vybaveni/1-rocnik/06-spravce-uloh.en.md new file mode 100644 index 0000000..b9e7a54 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/06-spravce-uloh.en.md @@ -0,0 +1,6 @@ +--- +nazev: Task Manager +popis: A process, memory and startup programs. The system is not ended. +nazev_cviceni: Quiz: Task Manager +nadpis_kvizu: Pick what you may end +--- diff --git a/content/programove-vybaveni/1-rocnik/06-spravce-uloh.md b/content/programove-vybaveni/1-rocnik/06-spravce-uloh.md new file mode 100644 index 0000000..45405b1 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/06-spravce-uloh.md @@ -0,0 +1,79 @@ +--- +id: sw-1-spravce-uloh +nazev: Správce úloh +popis: Proces, paměť a programy po spuštění. Systém se neukončuje. +nazev_cviceni: Kvíz: Správce úloh +nadpis_kvizu: Vyberte, co smíte ukončit +--- + +# Výklad + +## Okno +*Ctrl+Shift+Esc* + +- Správce úloh se otevře Ctrl+Shift+Esc. Nehledá se přes náhodné ikony na ploše. +- Karta Procesy ukáže aplikace a to, co běží na pozadí. +- Sloupec Procesor je v procentech. Sloupec Paměť je v megabajtech. +- Vysoká paměť u prohlížeče s dvaceti kartami je běžná. Vysoká paměť neznámého procesu se hlásí. + +> Tip: Když plocha nereaguje, Ctrl+Shift+Esc často projde i tehdy, když nabídka Start ne. + +## Ukončit se smí aplikace +*Ne systém* + +- Ukončit úlohu smíte u svého Wordu, který nereaguje. Rozpracovaný dokument si předtím uložte, jinak o něj přijdete. +- Proces Systém, Registr a aplikace systému Windows se neukončují. Počítač pak nemusí doběhnout do restartu. +- Neznámý proces nejdřív vyhledejte podle přesného názvu. Ukončení není první krok. +- Restart počítače je čistší než ukončit deset systémových procesů za sebou. + +> Tip: Word, který nereaguje, ukončete a otevřete znovu. Autorecover často dokument vrátí, ale nespoléhejte na něj. + +## Po spuštění +*Co startuje samo* + +- Karta Aplikace po spuštění ukáže programy, které naběhnou s Windows. +- Každý takový program bere čas startu a paměť. Ve škole jich má být málo. +- Vypnutí Teams nebo prohlížeče po spuštění, když ho učitel nechce, je v pořádku. Vypnutí zabezpečení Windows není. +- Změna se projeví po příštím přihlášení, ne vždy hned. + +> Tip: Povolené a zakázané je v posledním sloupci. Zakázaný program v počítači zůstane, jen se sám nespustí. + +## Zadání: jeden pohled +*Cvičení* + +- Otevřete Správce úloh a zapište tři aplikace, které berou nejvíc paměti. Čísla opište. +- Najděte, jestli s Windows startuje něco, co nepoznáváte. Neznámé nevypínejte, jen ho zapište. +- Systémové procesy neukončujte. Když Word nereaguje, ukončete jen Word. +- Uložte procesy.txt do Skola\1-rocnik\system. + +> Tip: Řazení podle paměti je kliknutí na záhlaví sloupce Paměť. První řádek je pak ten nejtěžší. + +# Kvíz + +## Jakou zkratkou se otevře Správce úloh? +- [x] Ctrl+Shift+Esc +- Ctrl+S +- Win+E je Správce úloh +- Alt+F4 na ploše +> Ctrl+Shift+Esc otevře Správce přímo. Win+E je Průzkumník. + +## Co smíte ukončit, když nereaguje vaše práce? +- Proces Systém +- [x] Svůj Word, který nereaguje +- Registr +- Zabezpečení Windows +> Ukončuje se zaseknutá aplikace. Systémové procesy drží počítač. + +## Co dělá vypnutí programu na kartě Po spuštění? +- Odinstaluje ho +- [x] Příště se sám nespustí s Windows +- Smaže Dokumenty +- Zakáže aktualizace +> Program v počítači zůstane. Nenaběhne jen sám při přihlášení. + +## Vysoká paměť u prohlížeče s mnoha kartami znamená co? +- Že se má hned ukončit Systém +- [x] Že prohlížeč prostě drží otevřené karty +- Že je disk NTFS rozbitý +- Že vypršela licence +> Každá karta bere paměť. Neznámý proces je jiný případ než prohlížeč, který jste sami otevřeli. diff --git a/content/programove-vybaveni/1-rocnik/07-zabezpeceni.en.md b/content/programove-vybaveni/1-rocnik/07-zabezpeceni.en.md new file mode 100644 index 0000000..aaec038 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/07-zabezpeceni.en.md @@ -0,0 +1,6 @@ +--- +nazev: Securing the computer +popis: Antivirus, updates and a file that pretends to be a document. +nazev_cviceni: Quiz: security +nadpis_kvizu: Pick what stays on and what stays closed +--- diff --git a/content/programove-vybaveni/1-rocnik/07-zabezpeceni.md b/content/programove-vybaveni/1-rocnik/07-zabezpeceni.md new file mode 100644 index 0000000..803fd77 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/07-zabezpeceni.md @@ -0,0 +1,79 @@ +--- +id: sw-1-zabezpeceni +nazev: Zabezpečení počítače +popis: Antivir, aktualizace a soubor, který se tváří jako dokument. +nazev_cviceni: Kvíz: zabezpečení +nadpis_kvizu: Vyberte, co se nezapíná a co se neotvírá +--- + +# Výklad + +## Zabezpečení Windows +*Defender* + +- Školní Windows mají Zabezpečení Windows. Antivir se nevypíná kvůli instalaci hry. +- Ochrana v reálném čase má být zapnutá. Vypnutá ochrana je nález, ne úleva. +- Brána firewall má být zapnutá na školní síti. Vypnutí kvůli jedné hře se nedělá. +- Druhý antivir vedle Defenderu se bez učitele neinstaluje. Dva se perou a oba pak hlídají hůř. + +> Tip: Zabezpečení Windows otevřete z nabídky Start. Zelené fajfky u položek jsou stav, který opisujete do úkolu. + +## Soubory a maily +*Co neotvírat* + +- Příloha faktura.pdf.exe se neotevírá, ani když text mailu zní věcně. +- Odkaz na přihlášení, který nečekáte, se neotvírá. Škola po hesle v mailu nepátrá. +- Flash disk neznámého původu se do školního počítače nestrká. Nejdřív učitel. +- Makro ve Wordu se povoluje, jen když dokument přišel ze školy a učitel řekl proč. + +> Tip: Heslo se neposílá mailem ani do chatu. Kdo ho po vás chce zprávou, nemá ho dostat. + +## Oprávnění k souboru +*NTFS* + +- Pravý klik na složku, Vlastnosti, Zabezpečení ukáže, kdo smí číst a kdo zapisovat. +- Běžný uživatel zapíše do svých Dokumentů. Do C:\Windows nezapíše. +- Odebrat si vlastní právo číst vlastní Dokumenty je chyba. Složka pak nejde otevřít. +- Společná složka Public nemá být místo, kam dáváte test s jménem. + +> Tip: Když soubor nejde uložit, často nemáte právo zápisu. Uložte ho do Dokumentů, ne do Program Files. + +## Zadání: pět kontrol +*Cvičení* + +- Opište, jestli je ochrana v reálném čase zapnutá a jestli firewall hlásí zapnuto. +- Z Windows Update opište, jestli něco čeká na restart. +- Najděte ve stažených souborech, jestli tam není soubor s dvojitou příponou. Neotvírejte ho, zapište název. +- Uložte bezpecnost.txt do Skola\1-rocnik\system. Ochranu nevypínejte. + +> Tip: Když je ochrana vypnutá, nezapínejte ji naslepo třemi kliky v neznámém okně. Řekněte to učiteli, může ji vypínat školní správa. + +# Kvíz + +## Co má být zapnuté v Zabezpečení Windows? +- Nic, antivir zpomaluje Word +- [x] Ochrana v reálném čase a firewall +- Jen spořič obrazovky +- Jen OneDrive +> Antivir i firewall jsou součást školního počítače. Nevypínají se kvůli pohodlí. + +## Který soubor neotevřete? +- poznamka.txt z vlastní složky +- [x] faktura.pdf.exe z mailu +- zadost.docx, kterou jste sami uložili +- znamky.xlsx z hodiny +> Dvojitá přípona exe je program. Mail s fakturou ho nesmí spustit. + +## Kam běžný uživatel smí uložit úkol? +- Do C:\Windows +- [x] Do svých Dokumentů +- Do složky ovladačů +- Přes cizí účet +> Dokumenty vlastního účtu jsou na zápis. Systémové složky ne. + +## Proč se vedle Defenderu bez učitele nedává druhý antivir? +- Windows umí jen jeden a druhý se vždy sám smaže +- [x] Dva antiviry se navzájem ruší +- Defender je freeware bez licence +- Excel pak neumí SUMA +> Dva programy, které hlídají stejné soubory, se blokují. Ve škole stačí Zabezpečení Windows. diff --git a/content/programove-vybaveni/1-rocnik/08-zadani-windows.en.md b/content/programove-vybaveni/1-rocnik/08-zadani-windows.en.md new file mode 100644 index 0000000..847262b --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/08-zadani-windows.en.md @@ -0,0 +1,6 @@ +--- +nazev: Assignment: a school account and a tidy disk +popis: Folders, a standard account, updates and protection in one note. +nazev_cviceni: Quiz: the school account +nadpis_kvizu: Check the whole assignment +--- diff --git a/content/programove-vybaveni/1-rocnik/08-zadani-windows.md b/content/programove-vybaveni/1-rocnik/08-zadani-windows.md new file mode 100644 index 0000000..862c127 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/08-zadani-windows.md @@ -0,0 +1,79 @@ +--- +id: sw-1-zadani-windows +nazev: Zadání: školní účet a pořádek +popis: Složky, běžný účet, aktualizace a ochrana v jednom zápisu. +nazev_cviceni: Kvíz: školní účet +nadpis_kvizu: Zkontrolujte celé zadání +--- + +# Výklad + +## Složky +*Strom* + +- V Dokumentech je Skola\1-rocnik a v ní word, excel a system. +- Na ploše nezůstane pracovní soubor. Plocha není archiv. +- Přípony názvů jsou vidět. poznámka se nejmenuje poznámka.txt.txt. +- Názvy jsou bez znaků \ / : * ? a datum v názvu je rok-měsíc-den. + +> Tip: Cestu opište z řádku adresy. Od oka se lomítka pletou. + +## Účet +*zaci* + +- Cvičný účet zaci je běžný uživatel. Není ve skupině Administrators. +- Heslo má aspoň 8 znaků a nezadává se do příkazu, jen přes hvězdičku v net user zaci * /add. +- Do zápisu nepatří skutečné heslo. Patří tam pravidlo a příkaz bez hesla. +- whoami na denní práci nemá ukázat účet správce. + +> Tip: Účet zakládejte jen pod dohledem. V zápisu stačí postup, když učitel účet na počítači nechce. + +## Stav systému +*Tři řádky* + +- winver: edice a verze. +- Windows Update: aktuální, nebo čeká restart. Datum stačí. +- Zabezpečení Windows: ochrana v reálném čase zapnutá, firewall zapnutý. +- Ze Správce úloh tři nejtěžší aplikace podle paměti. Systémové procesy neukončené. + +> Tip: Když je něco vypnuté, zapište to jako nález. Neopravujte firewall naslepo. + +## Odevzdání +*Jeden soubor* + +- Všechno je v ucto-windows.txt ve složce Skola\1-rocnik\system. +- Jsou tam cesta ke složce word, popis účtu, verze Windows a stav ochrany. +- Není tam heslo, není tam cizí soubor a není tam vypnutý Defender. +- Soubor otevřete znovu a přečtěte ho. Cesta v textu musí po vložení do adresy opravdu vyjít. + +> Tip: Když cesta z textu skončí chybou, složka neexistuje, nebo je v cestě obyčejné lomítko. + +# Kvíz + +## Jaký účet je zaci? +- Správce +- [x] Běžný uživatel mimo skupinu Administrators +- Účet bez hesla v Public +- Sdílený účet celé třídy +> Denní účet není správce. Heslo má a do skupiny Administrators nepatří. + +## Kam se ukládá pracovní soubor? +- Na plochu +- [x] Do Skola\1-rocnik a příslušné složky +- Do C:\Windows +- Do stažených souborů bez názvu +> Plocha se nearchivuje. Strom Skola drží word, excel a system odděleně. + +## Co má být zapnuté? +- Jen spořič +- [x] Ochrana v reálném čase a firewall +- Dva antiviry +- Sdílení celé složky Users +> Defender a firewall zůstávají zapnuté. Do úkolu se opisuje jejich stav. + +## Co v souboru ucto-windows.txt být nesmí? +- Verze z winver +- [x] Skutečné heslo +- Cesta ke složce +- Stav Windows Update +> Heslo se do textového úkolu nepíše. Příkaz ho bere přes hvězdičku, ne do sešitu. diff --git a/content/programove-vybaveni/2-rocnik/01-word-styly.en.md b/content/programove-vybaveni/2-rocnik/01-word-styly.en.md new file mode 100644 index 0000000..92c3344 --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/01-word-styly.en.md @@ -0,0 +1,6 @@ +--- +nazev: Word: styles +popis: Heading 1, body text and spacing that is not empty lines. +nazev_cviceni: Quiz: styles +nadpis_kvizu: Pick the style and the shortcut +--- diff --git a/content/programove-vybaveni/2-rocnik/01-word-styly.md b/content/programove-vybaveni/2-rocnik/01-word-styly.md new file mode 100644 index 0000000..b0a2f8d --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/01-word-styly.md @@ -0,0 +1,79 @@ +--- +id: sw-2-word-styly +nazev: Word: styly +popis: Nadpis 1, normální text a mezery, které nejsou prázdné řádky. +nazev_cviceni: Kvíz: styly +nadpis_kvizu: Vyberte styl a zkratku +--- + +# Výklad + +## Styl, ne ruční tučné +*Nadpis* + +- Nadpis 1 je styl. Ctrl+Alt+1 ho zapne. Nadpis 2 je Ctrl+Alt+2. +- Tučné a větší písmo místo stylu vypadá jako nadpis, ale obsah z něj Word nesestaví. +- Normální text je styl Normální. Ctrl+Shift+N ho vrátí, když jste omylem v nadpisu. +- V dokumentu jsou nejvýš tři úrovně nadpisů. Čtvrtá je už nepřehledná. + +> Tip: Podokno Styly otevřete z karty Domů. U odstavce je vidět, jaký styl opravdu má. + +## Odstavec +*Mezery ze stylu* + +- Prázdný Enter mezi odstavci se nedělá. Mezera se nastaví ve stylu, třeba 8 bodů za odstavcem. +- Řádkování školního textu je 1,15 nebo 1,5. Ne dvojité přes celou stránku bez důvodu. +- Zarovnání do bloku u delšího textu, vlevo u nadpisu a u krátkého dopisu. +- Stránka je A4. Okraje 2,5 cm ze všech stran, to je předvolba Normální. + +> Tip: Konec stránky je Ctrl+Enter. Řada Enterů na další stránku se při úpravě rozjede. + +## Soubor +*docx a pdf* + +- Pracovní soubor je .docx. Jdou v něm styly i další úpravy. +- Na odevzdání, které se už nemá měnit, je navíc PDF. +- Jednou vyexportované PDF neupravujte dál jako jediný zdroj. Opravte docx a PDF udělejte znovu. +- Název je věcný: 2026-06-12-protokol.docx. Dokument1.docx se neodevzdává. + +> Tip: Soubor, Export, Vytvořit soubor PDF. Před exportem se podívejte na náhled tisku, jestli nadpis nesedí sám na konci stránky. + +## Zadání: dvoustránkový text +*Cvičení* + +- Dokument A4, okraje 2,5 cm, aspoň tři nadpisy stylem Nadpis 1. +- Pod každým nadpisem aspoň jeden odstavec stylem Normální. Žádné prázdné Entery mezi nimi. +- Konec první stránky je Ctrl+Enter, ne deset prázdných řádků. +- Uložte protokol.docx do Skola\1-rocnik\word a k němu protokol.pdf. + +> Tip: Když v navigačním podokně nadpisy nejsou, jsou to jen tučná písma. Označte je a dejte Ctrl+Alt+1. + +# Kvíz + +## Jak se má udělat nadpis, aby z něj šel obsah? +- Tučně a 16 bodů +- [x] Stylem Nadpis 1, zkratka Ctrl+Alt+1 +- Pěti prázdnými Entery nad ním +- Obrázkem textu +> Obsah čte styly. Ruční tučné písmo v obsahu není. + +## Čím se přejde na další stránku? +- Deseti Entery +- [x] Ctrl+Enter +- Mezerou za odstavcem 80 bodů +- Smazáním okrajů +> Ctrl+Enter je konec stránky. Prázdné řádky se při doplnění textu rozjedou. + +## Jaké jsou školní okraje na A4? +- 0 cm, ať se vejde víc +- [x] 2,5 cm, předvolba Normální +- 5 cm vpravo a 0 vlevo +- Okraje se ve Wordu nenastavují +> Normální okraj je 2,5 cm. Text až do kraje papíru tiskárna často ořízne. + +## Který soubor se dál upravuje? +- PDF +- [x] docx +- Jen vytištěná stránka +- Příloha exe +> docx drží styly. PDF je kopie na odevzdání. diff --git a/content/programove-vybaveni/2-rocnik/02-word-tabulka-a-obsah.en.md b/content/programove-vybaveni/2-rocnik/02-word-tabulka-a-obsah.en.md new file mode 100644 index 0000000..9a94510 --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/02-word-tabulka-a-obsah.en.md @@ -0,0 +1,6 @@ +--- +nazev: Word: a table, a header and a table of contents +popis: A 4 by 3 table, a page number and a contents list from the headings. +nazev_cviceni: Quiz: contents +nadpis_kvizu: Pick where the contents come from +--- diff --git a/content/programove-vybaveni/2-rocnik/02-word-tabulka-a-obsah.md b/content/programove-vybaveni/2-rocnik/02-word-tabulka-a-obsah.md new file mode 100644 index 0000000..ce559e7 --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/02-word-tabulka-a-obsah.md @@ -0,0 +1,79 @@ +--- +id: sw-2-word-obsah +nazev: Word: tabulka, záhlaví a obsah +popis: Tabulka 4×3, číslo stránky a obsah z nadpisů. +nazev_cviceni: Kvíz: obsah +nadpis_kvizu: Vyberte, odkud se obsah vezme +--- + +# Výklad + +## Tabulka +*4 řádky a 3 sloupce* + +- Tabulka se vkládá z karty Vložení. Kreslení tužkou od ruky se ve školním protokolu nedělá. +- Školní tabulka má 4 řádky a 3 sloupce: záhlaví a tři záznamy. +- Záhlaví tabulky je tučné a řádek se neopakuje jako obyčejný text pod tabulkou. +- Text v buňce se nezalamuje deseti mezerami. Šířka sloupce se táhne myší. + +> Tip: Když potřebujete řádek navíc, klepněte vpravo od řádku a dejte vložit. Enter v buňce jen zalomí text. + +## Záhlaví a zápatí +*Číslo stránky* + +- Záhlaví je nahoře, zápatí dole. Dvojklik v okraji je otevře. +- V záhlaví je název školy a název dokumentu. V zápatí je číslo stránky. +- Číslo stránky se vloží jako pole. Ručně napsaná jednička se na druhé stránce neposune. +- Záhlaví není nadpis dokumentu. Nadpis zůstává v textu jako Nadpis 1. + +> Tip: Pole čísla stránky má po označení šedé pozadí. Když je to obyčejná číslice, na další stránce se nezmění. + +## Obsah +*Z nadpisů* + +- Obsah je na kartě Reference, Obsah. Word ho složí z Nadpis 1 a Nadpis 2. +- Ručně psaný seznam stránek obsah není. Po vložení odstavce se rozejde. +- Po změně textu se obsah aktualizuje pravým klikem, Aktualizovat pole, a volbou celá tabulka. +- Obsah stojí na začátku, za titulní řádek, na vlastní stránce nebo hned pod názvem. Ne na konci. + +> Tip: Když je obsah prázdný, nadpisy nejsou styly. Ctrl+Alt+1 a obsah aktualizujte znovu. + +## Zadání: protokol s tabulkou +*Cvičení* + +- Vezměte protokol.docx. Přidejte tabulku 4×3: sloupec Jméno, Třída, Stav. +- Tři řádky: Anna Nováková, 1.A, odevzdáno. Druhý a třetí řádek doplňte vlastními jmény. +- Záhlaví s názvem školy, zápatí s číslem stránky, obsah z nadpisů na začátku. +- Uložte znovu docx a nové PDF. Staré PDF přepište, ať neexistují dvě verze. + +> Tip: Aktualizace obsahu se ptá, jestli jen čísla, nebo celá tabulka. Po nových nadpisech berte celou tabulku. + +# Kvíz + +## Jak velká je školní tabulka v tomto úkolu? +- [x] 4 řádky a 3 sloupce +- Jedna buňka +- 10 sloupců bez záhlaví +- Tabulka se ve Wordu nedělá +> Záhlaví plus tři záznamy jsou čtyři řádky. Sloupce jsou Jméno, Třída a Stav. + +## Odkud Word vezme obsah? +- Z tučných vět +- [x] Ze stylů Nadpis 1 a Nadpis 2 +- Z zápatí +- Z Excelu automaticky vždy +> Bez stylů nadpisů je obsah prázdný. Tučné písmo nestačí. + +## Jak se má dostat na stránku číslo? +- Napsat 1 do zápatí ručně +- [x] Vložit pole čísla stránky +- Dát ho do názvu souboru +- Nepsat ho, Word ho do PDF doplní sám i bez pole +> Pole se na každé stránce samo zvýší. Ruční jednička zůstane jednička. + +## Kdy se obsah aktualizuje celý, ne jen čísla stránek? +- [x] Když přibyl nadpis +- Když se jen změnila šířka okraje o milimetr a nadpisy zůstaly +- Po každém písmenu +- Obsah se neaktualizuje nikdy +> Nový nadpis do obsahu přibude jen při aktualizaci celé tabulky. diff --git a/content/programove-vybaveni/2-rocnik/03-word-dopis.en.md b/content/programove-vybaveni/2-rocnik/03-word-dopis.en.md new file mode 100644 index 0000000..6a953ff --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/03-word-dopis.en.md @@ -0,0 +1,6 @@ +--- +nazev: Word: a formal letter +popis: Sender, date, subject, greeting, body and signature on one page. +nazev_cviceni: Quiz: the letter +nadpis_kvizu: Put the parts in order +--- diff --git a/content/programove-vybaveni/2-rocnik/03-word-dopis.md b/content/programove-vybaveni/2-rocnik/03-word-dopis.md new file mode 100644 index 0000000..288e40f --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/03-word-dopis.md @@ -0,0 +1,79 @@ +--- +id: sw-2-word-dopis +nazev: Word: úřední dopis +popis: Odesílatel, datum, věc, oslovení, text a podpis na jedné straně. +nazev_cviceni: Kvíz: dopis +nadpis_kvizu: Seřaďte náležitosti +--- + +# Výklad + +## Náležitosti +*Shora dolů* + +- Vlevo nahoře odesílatel: škola, jméno, adresa. Vpravo datum 12. 6. 2026. +- Pod tím adresát. Pak řádek Věc: žádost o uvolnění z výuky. +- Oslovení, text, rozloučení a jméno s podpisem. Podpis je pod jménem, ne místo něj. +- Celý dopis je jedna strana A4. Druhá stránka s jedním řádkem je chyba. + +> Tip: Datum pište 12. 6. 2026, ne 12/6/26. Český úřední dopis má den, měsíc a rok. + +## Vzhled +*Klidný dopis* + +- Písmo jedno, třeba Calibri 11 nebo 12. V dopise není Nadpis 1 přes celou šířku. +- Řádek Věc je tučný nebo jako krátký nadpis, zbytek je Normální. +- Odstavce mají mezeru za sebou. Mezi oslovením a textem není deset Enterů. +- Okraje 2,5 cm. Text do bloku. Bez obrázku, bez tabulky, bez obsahu. + +> Tip: Když se poslední řádek podpisu sleze na druhou stránku, zkraťte mezeru za odstavcem, nepřidávejte menší okraje než 2 cm. + +## Hromadná pošta +*Až když je řádků víc* + +- Jeden dopis se píše přímo. Hromadná korespondence se vyplatí, když je adresátů aspoň deset. +- Zdroj dat je Excel s řádkem záhlaví: Jmeno, Prijmeni, Trida. +- Word na kartě Korespondence vezme tento sešit a pole vloží do oslovení. +- Sloučený výsledek se ukládá jako nový docx. Zdrojový dopis s poli si nechte zvlášť. + +> Tip: Když Excel nemá záhlaví, první žák se stane názvem sloupce a z hromadné pošty vypadne. + +## Zadání: žádost +*Cvičení* + +- Napište žádost o uvolnění z výuky na 12. 6. 2026. Adresát je ředitel školy, odesílatel jste vy. +- V dokumentu je Věc, oslovení, aspoň dvě věty důvodu a podpis. +- Jedna strana, A4, okraje 2,5 cm. Uložte zadost.docx a zadost.pdf do složky word. +- Hromadnou poštu v tomto úkolu nedělejte. Stačí jeden dopis. + +> Tip: Přečtěte dopis nahlas. Když v něm chybí, o co žádáte a který den, věta důvodu nestačí. + +# Kvíz + +## Kam patří datum v tomto dopisu? +- Dospodu místo podpisu +- [x] Dopravna, 12. 6. 2026 +- Do názvu souboru jako jediné místo +- Do zápatí místo čísla, datum v textu být nesmí +> Datum je vpravo nahoře. Podpis dole je jméno, ne datum. + +## Jak se označí, o co v dopise jde? +- [x] Řádkem Věc +- Stylem Nadpis 3 uprostřed stránky +- Obrázkem razítka +- Obsahem na druhé straně +> Věc je krátký řádek nad oslovením, třeba žádost o uvolnění z výuky. + +## Kdy se použije hromadná korespondence? +- Na jeden dopis řediteli +- [x] Když je stejný dopis pro deset a víc adresátů a data jsou v Excelu +- Na každou tabulku +- Místo PDF +> Jeden dopis se píše ručně. Hromadná pošta bere řádky z Excelu se záhlavím. + +## Jak dlouhý je školní dopis z tohoto zadání? +- [x] Jedna strana A4 +- Přesně tři strany +- Půl stránky a zbytek prázdných Enterů +- Deset stran se styly Nadpis 1 +> Žádost se vejde na jednu stranu. Druhá stránka jen s podpisem je špatně zalomená. diff --git a/content/programove-vybaveni/2-rocnik/04-excel-bunky.en.md b/content/programove-vybaveni/2-rocnik/04-excel-bunky.en.md new file mode 100644 index 0000000..17e029c --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/04-excel-bunky.en.md @@ -0,0 +1,6 @@ +--- +nazev: Excel: cells and formulas +popis: The address A1, a formula with an equals sign and a dollar lock. +nazev_cviceni: Quiz: cells +nadpis_kvizu: Complete the formula +--- diff --git a/content/programove-vybaveni/2-rocnik/04-excel-bunky.md b/content/programove-vybaveni/2-rocnik/04-excel-bunky.md new file mode 100644 index 0000000..9ac18a5 --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/04-excel-bunky.md @@ -0,0 +1,79 @@ +--- +id: sw-2-excel-bunky +nazev: Excel: buňky a vzorce +popis: Adresa A1, vzorec se rovnítkem a ukotvení dolarama. +nazev_cviceni: Kvíz: buňky +nadpis_kvizu: Doplňte vzorec +--- + +# Výklad + +## Buňka +*Adresa* + +- Sloupce jsou písmena, řádky čísla. B2 je druhý sloupec a druhý řádek. +- Do buňky patří jedna hodnota. Jméno a třída do jedné buňky nepatří. +- Číslo je číslo, text je text. Jeden apostróf před číslem z něj udělá text a přestane se sčítat. +- Sešit je soubor xlsx. List je záložka dole. Školní sešit má listy Ceník a Součet. + +> Tip: Klik na záhlaví sloupce označí celý sloupec. Na číslo řádku celý řádek. + +## Vzorec +*Rovnítko* + +- Vzorec začíná =. Bez rovnítka je B2*C2 jen text a nic nespočítá. +- V ceníku je v D2 vzorec =B2*C2. B je počet, C je cena, D je částka. +- Český Excel odděluje argumenty funkcí středníkem. Krát je hvězdička, děleno lomítko. +- Vzorec se kopíruje za úchyt v pravém dolním rohu buňky. Neopisuje se do pěti buněk ručně. + +> Tip: Když buňka ukáže vzorec místo výsledku, je sloupec úzký, nebo je buňka text. Zkontrolujte rovnítko. + +## Posun a dolar +*A1 a $A$1* + +- Zkopírování =B2*C2 o řádek níž udělá =B3*C3. Adresy se posunou. +- =$B$2 se neposune ani ve sloupci, ani v řádku. Dolar kotví. +- Sazba DPH v buňce F1 se ve vzorcích bere jako $F$1, ať kopírování neodjede na F2. +- Smazání hodnoty, na kterou vzorec ukazuje, udělá chybu. Vzorec na prázdnou buňku není nula sám od sebe, když čekal číslo a dostal text. + +> Tip: Dolar jen u písmene, $B2, kotví sloupec. Dolar jen u čísla, B$2, kotví řádek. + +## Zadání: ceník +*Cvičení* + +- List Ceník: sloupce Polozka, Pocet, Cena, Castka. Záhlaví v řádku 1, pět položek v řádcích 2 až 6. +- Castka v D2 je =B2*C2 a dolů se táhne. Žádná částka není napsaná ručně. +- Sazba v F1 je 0,21 a v E2 je =D2*$F$1. Po zkopírování dolů pořád ukazuje na F1. +- Uložte cenik.xlsx do Skola\1-rocnik\excel. + +> Tip: Klik na D2 ukáže vzorec v řádku vzorců. Když tam je jen číslo, částka je opsaná a při změně počtu se nehne. + +# Kvíz + +## Čím začíná vzorec? +- Závorkou +- [x] Rovnítkem +- Středníkem +- Dolarama bez rovnítka +> Bez = Excel nic nepočítá. Text B2*C2 zůstane text. + +## Co je v D2, když počet je v B2 a cena v C2? +- [x] =B2*C2 +- B2*C2 bez rovnítka +- =B2+C2 +- Ručně spočítané číslo +> Částka je součin. Sčítání by sečetlo počet s cenou, což není částka. + +## Proč je sazba ve vzorci $F$1? +- [x] Aby se při kopírování dolů adresa neposunula +- Protože dolar je měna tabulky +- Aby vzorec přestal počítat +- $ se v Excelu nesmí +> F1 je jedna buňka pro všechny řádky. Bez dolarů by druhý řádek četl F2. + +## Co udělá zkopírování =B2*C2 o řádek níž? +- Nechá B2 a C2 +- [x] Vznikne =B3*C3 +- Smaže list +- Změní se na text +> Relativní adresa se posune o stejný počet řádků jako kopírování. diff --git a/content/programove-vybaveni/2-rocnik/05-excel-funkce.en.md b/content/programove-vybaveni/2-rocnik/05-excel-funkce.en.md new file mode 100644 index 0000000..06d600d --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/05-excel-funkce.en.md @@ -0,0 +1,6 @@ +--- +nazev: Excel: functions +popis: SUM, IF and VLOOKUP. The Czech edition separates arguments with a semicolon. +nazev_cviceni: Quiz: functions +nadpis_kvizu: Pick the right formula +--- diff --git a/content/programove-vybaveni/2-rocnik/05-excel-funkce.md b/content/programove-vybaveni/2-rocnik/05-excel-funkce.md new file mode 100644 index 0000000..67c6907 --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/05-excel-funkce.md @@ -0,0 +1,79 @@ +--- +id: sw-2-excel-funkce +nazev: Excel: funkce +popis: SUMA, KDYŽ a SVYHLEDAT. V české verzi se argumenty oddělují středníkem. +nazev_cviceni: Kvíz: funkce +nadpis_kvizu: Vyberte správný zápis +--- + +# Výklad + +## Souhrny +*SUMA a spol.* + +- SUMA(D2:D6) sečte částky. Průměr je PRŮMĚR, nejmenší MIN, největší MAX. +- POČET spočítá čísla. Prázdnou buňku nepočítá. Text nepočítá. +- Rozsah D2:D6 zahrnuje oba konce. D2:D6 jsou řádky 2 až 6, ne jen 2 a 6. +- Souhrn patří pod tabulku, nebo na list Součet. Nepočítá se na papír a neopisuje se. + +> Tip: Anglický Excel má SUM a čárku. Český má SUMA a středník. Ve škole pište česky, jak to má školní Office. + +## KDYŽ +*Známky* + +- České známky: 1 je nejlepší, 5 je neprospěl. Prospěch je známka 1 až 4. +- =KDYŽ(D2<=4;"prospěl";"neprospěl") vrací text podle známky v D2. +- Středníky oddělují podmínku, větev ano a větev ne. Čárka v české verzi vzorec rozbije. +- Podmínka, která čeká známku aspoň 4 jako úspěch, je obráceně. Čtyřka ještě prochází, pětka ne. + +> Tip: Uvozovky kolem textu ve funkci jsou povinné. Bez nich Excel hledá buňku nebo název. + +## SVYHLEDAT +*Ceník* + +- SVYHLEDAT najde hodnotu v levém sloupci oblasti a vrátí jiný sloupec stejného řádku. +- =SVYHLEDAT(A2;Ceník!$A$2:$C$20;3;NEPRAVDA) hledá A2, oblast je ukotvená, vrátí 3. sloupec, shoda je přesná. +- NEPRAVDA znamená přesnou shodu. PRAVDA by směla vzít nejbližší nižší a u textu plete. +- Hledaná hodnota musí být v prvním sloupci oblasti. Jinak funkce nehledá uprostřed tabulky. + +> Tip: Když funkce vrátí #N/A, hodnota v prvním sloupci není. Zkontrolujte mezeru za jménem a stejný typ, text proti číslu. + +## Zadání: známky a ceník +*Cvičení* + +- Na list Znamky dejte pět žáků, sloupec Znamka a sloupec Stav se vzorcem KDYŽ, prospěl při známce 1 až 4. +- Pod známky SUMA nedávejte, součet známek nic neříká. Dejte PRŮMĚR. +- Na listu Ceník doplňte SVYHLEDAT, který podle kódu položky vrátí cenu. Oblast ukotvěte a dejte NEPRAVDA. +- Uložte znamky.xlsx do složky excel. Oba listy ať jsou v jednom sešitu. + +> Tip: Průměr známek 1,0 až 5,0 vychází jako číslo. Když vyjde 0, počítáte prázdné buňky, nebo máte text místo čísel. + +# Kvíz + +## Jak se v českém Excelu zapíše součet D2 až D6? +- =SUM(D2:D6) +- [x] =SUMA(D2:D6) +- =SUMA(D2,D6) +- SUMA(D2:D6) bez rovnítka +> Česká funkce je SUMA a rozsah je D2:D6. SUM je anglická verze. + +## Který vzorec označí známku 5 jako neprospěl? +- [x] =KDYŽ(D2<=4;"prospěl";"neprospěl") +- =KDYŽ(D2>=4;"prospěl";"neprospěl") +- =KDYŽ(D2<=4,"prospěl","neprospěl") +- =D2<=4 +> Prochází 1 až 4, tedy menší nebo rovno 4. V češtině jsou středníky, ne čárky. + +## Co znamená NEPRAVDA na konci SVYHLEDAT? +- Vypne vzorec +- [x] Chce přesnou shodu +- Sečte sloupec +- Vrátí vždy první řádek +> NEPRAVDA je přesně ta hodnota. PRAVDA bere přibližnou shodu a u kódů položek lže. + +## Proč je oblast Ceník!$A$2:$C$20 ukotvená? +- [x] Aby se při kopírování vzorce dolů neposunula +- Protože jinak nejde uložit xlsx +- Dolar sčítá ceny +- SVYHLEDAT ukotvení zakazuje +> Každý řádek hledá v téže tabulce. Bez dolarů by se oblast sunula dolů s vzorcem. diff --git a/content/programove-vybaveni/2-rocnik/06-excel-grafy.en.md b/content/programove-vybaveni/2-rocnik/06-excel-grafy.en.md new file mode 100644 index 0000000..a159302 --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/06-excel-grafy.en.md @@ -0,0 +1,6 @@ +--- +nazev: Excel: charts +popis: A column chart to compare, a line chart for time. A pie only for parts of a whole. +nazev_cviceni: Quiz: charts +nadpis_kvizu: Pick the chart type +--- diff --git a/content/programove-vybaveni/2-rocnik/06-excel-grafy.md b/content/programove-vybaveni/2-rocnik/06-excel-grafy.md new file mode 100644 index 0000000..4eda9dd --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/06-excel-grafy.md @@ -0,0 +1,79 @@ +--- +id: sw-2-excel-grafy +nazev: Excel: grafy +popis: Sloupcový graf pro porovnání, spojnicový pro čas. Koláč jen pro podíl celku. +nazev_cviceni: Kvíz: grafy +nadpis_kvizu: Vyberte typ grafu +--- + +# Výklad + +## Který graf +*Typ* + +- Porovnání tříd nebo položek je sloupcový graf. +- Vývoj po měsících je spojnicový. Osa x je čas, zleva doprava. +- Koláč je jen pro podíly jednoho celku, nejvýš pět výsečí. Dvanáct měsíců v koláči nečtěte. +- Dva grafy přes sebe, které říkají totéž, se nedělají. + +> Tip: Když váháte, sloupec je ve škole skoro vždycky bezpečnější než koláč. + +## Co musí být vidět +*Popisky* + +- Graf má název, třeba Tržba po měsících. Název Graf 1 se neodevzdává. +- Osa hodnot má jednotku, Kč nebo počet. Osa kategorií má názvy, ne 1 až 12 bez legendy měsíce, pokud to z dat není jasné. +- Legenda se dá pryč, když je řada jen jedna. Zabírá místo a nic neříká. +- Data grafu jsou v buňkách. Namalovat sloupec obrázkem nejde aktualizovat. + +> Tip: Klik na graf, Vybrat data. Když oblast nesedí, graf kreslí i záhlaví nebo prázdné řádky. + +## Odkud bere čísla +*Tabulka* + +- Nejdřív čísla, potom graf. Graf bez zdroje je obrázek. +- Nula v buňce je nula a v grafu je vidět. Prázdná buňka čáru přeruší. +- Text v číselném sloupci graf vynechá nebo ho položí jako nulu. Čísla nepište s jednotkou Kč v téže buňce. +- Změna čísla v tabulce změní graf. Když ne, graf je kopie jako obrázek. + +> Tip: Kč patří do záhlaví sloupce nebo do názvu osy, ne za číslo do buňky. Jinak je buňka text. + +## Zadání: jeden graf +*Cvičení* + +- Na novém listu Mesice dejte leden až červen a k nim tržbu v Kč, šest čísel. +- Vložte spojnicový graf s názvem Tržba leden až červen. Jednotka ať je v názvu osy nebo v názvu grafu. +- Koláč nedělejte. Legendu jedné řady vypněte. +- Uložte do znamky.xlsx jako další list, nebo do trzba.xlsx ve složce excel. + +> Tip: Náhled tisku ať graf neroztrhne na dvě stránky. Dejte ho pod tabulku, ne přes ni. + +# Kvíz + +## Jaký graf je na tržbu leden až červen? +- Koláč se šesti výsečemi +- [x] Spojnicový +- Náhodný 3D kužel +- Obrázek sloupců vložený z Wordu +> Čas jde zleva doprava ve spojnicovém grafu. Koláč neukáže pořadí měsíců. + +## Kdy je koláč na místě? +- Na dvanáct měsíců +- [x] Na podíly jednoho celku, jen pár výsečí +- Na každou tabulku +- Když chybí název +> Koláč je celek rozdělený na pár dílů. Časová řada do něj nepatří. + +## Kam nepatří Kč? +- Do názvu osy +- [x] Do buňky za číslo, třeba 120 Kč +- Do názvu grafu +- Do záhlaví sloupce +> 120 Kč je text. Graf i SUMA pak číslo nevidí. + +## Co se stane po změně čísla v tabulce, když je graf v pořádku? +- Graf se musí nakreslit znovu ručně +- [x] Sloupec nebo bod se změní sám +- Excel graf smaže +- Změní se licence +> Graf je navázaný na buňky. Obrázek vložený místo grafu se sám nezmění. diff --git a/content/programove-vybaveni/2-rocnik/07-excel-kontingence.en.md b/content/programove-vybaveni/2-rocnik/07-excel-kontingence.en.md new file mode 100644 index 0000000..bb01bed --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/07-excel-kontingence.en.md @@ -0,0 +1,6 @@ +--- +nazev: Excel: a table, a filter and a pivot +popis: A list as a table, a filter and a total by class. +nazev_cviceni: Quiz: the pivot table +nadpis_kvizu: Say what goes in rows and what goes in values +--- diff --git a/content/programove-vybaveni/2-rocnik/07-excel-kontingence.md b/content/programove-vybaveni/2-rocnik/07-excel-kontingence.md new file mode 100644 index 0000000..8f91004 --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/07-excel-kontingence.md @@ -0,0 +1,79 @@ +--- +id: sw-2-excel-kontingence +nazev: Excel: tabulka, filtr a kontingenční tabulka +popis: Seznam jako tabulka, filtr a součet podle třídy. +nazev_cviceni: Kvíz: kontingenční tabulka +nadpis_kvizu: Řekněte, co patří do řádků a co do hodnot +--- + +# Výklad + +## Tabulka +*Ctrl+T* + +- Seznam se záhlavím se označí a Ctrl+T z něj udělá tabulku. Filtr v záhlaví vznikne sám. +- Záhlaví je jeden řádek: Polozka, Trida, Castka. Dva řádky nadpisů Ctrl+T rozbije. +- Hned pod tabulku se nepíše další text. Souhrn je v řádku souhrnů, nebo na jiném listu. +- Řazení je šipka v záhlaví. Seřadit jen půlku sloupce mimo tabulku rozhodí řádky. + +> Tip: Tabulka dostane na kartě Návrh název, třeba Prodej. Pak se na ni dá odkazovat jménem. + +## Filtr +*Jen 1.A* + +- Filtr Trida = 1.A ostatní řádky schová. Nesmaže je. +- Zrušení filtru je Vybrat vše, nebo Vymazat filtr na kartě Data. +- Řádek souhrnů tabulky sečte jen to, co filtr nechá vidět. +- Obyčejná SUMA přes stejné buňky sečte i řádky, které filtr schoval. Na filtrovaný součet to není. + +> Tip: Když po filtru zmizí skoro všechno, podívejte se, jestli nemáte mezeru v názvu třídy. 1.A a 1.A s mezerou navíc jsou dvě hodnoty. + +## Kontingenční tabulka +*Součet podle třídy* + +- Kontingenční tabulka je na kartě Vložení. Zdroj je tabulka i se záhlavím. +- Do řádků přijde Trida. Do hodnot Součet z Castka. Sloupce nechte prázdné, dokud není druhý rozměr. +- Známky se v kontingenci nesčítají. U známek dejte průměr, u peněz součet. +- Kontingence patří na nový list. Když ji položíte přes zdroj, zdroj přepíšete. + +> Tip: Po změně dat dejte Obnovit. Kontingenční tabulka se sama nehne. + +## Zadání: prodej po třídách +*Cvičení* + +- List Prodej: aspoň 12 řádků, sloupce Polozka, Trida, Castka. Třídy jen 1.A a 1.B. +- Ctrl+T a název tabulky Prodej. Filtr ať umí ukázat jen 1.A a zase všechno. +- Kontingenční tabulka na novém listu: řádky Trida, hodnoty součet Castka. +- Uložte prodej.xlsx do složky Skola\1-rocnik\excel. Ruční součty vedle kontingence nepište. + +> Tip: Když kontingence ukáže Počet místo Součet, v hodnotách počítá buňky. Přepněte souhrn na Součet. + +# Kvíz + +## Co udělá Ctrl+T na seznamu se záhlavím? +- Smaže ho +- [x] Udělá z něj tabulku s filtrem +- Převede ho na PDF +- Uzamkne list heslem +> Tabulka má filtr v záhlaví a jde pojmenovat. Obyčejná oblast to nemá. + +## Co sečte jen řádky, které filtr nechá vidět? +- Obyčejná SUMA přes celou oblast +- [x] Řádek souhrnů tabulky +- Nadpis grafu +- Ručně opsaný součet +> SUMA schované řádky filtru pořád počítá. Řádek souhrnů tabulky bere jen viditelné. + +## Kam v kontingenční tabulce patří Trida a kam Castka? +- Trida do hodnot jako součet, Castka do řádků +- [x] Trida do řádků, součet Castka do hodnot +- Obojí do filtrů a nikam jinam +- Trida do názvu souboru +> Řádky jsou třídy. Hodnoty jsou součet peněz. Součet názvů tříd nedává smysl. + +## Kdy se u známek v kontingenci použije průměr? +- Vždy se známky sčítají +- [x] Když má vyjít průměrná známka, ne součet známek +- Průměr Excel neumí +- Jen u textu +> Součet známek nic neříká. Peníze se sčítají, známky se průměrují. diff --git a/content/programove-vybaveni/2-rocnik/08-zadani-office.en.md b/content/programove-vybaveni/2-rocnik/08-zadani-office.en.md new file mode 100644 index 0000000..04488f3 --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/08-zadani-office.en.md @@ -0,0 +1,6 @@ +--- +nazev: Assignment: a report and a sheet +popis: A Word letter and an Excel grade sheet in one folder. +nazev_cviceni: Quiz: the Office hand-in +nadpis_kvizu: Check both files +--- diff --git a/content/programove-vybaveni/2-rocnik/08-zadani-office.md b/content/programove-vybaveni/2-rocnik/08-zadani-office.md new file mode 100644 index 0000000..4c2a906 --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/08-zadani-office.md @@ -0,0 +1,79 @@ +--- +id: sw-2-zadani-office +nazev: Zadání: protokol a tabulka +popis: Dopis ve Wordu a známky v Excelu v jedné složce. +nazev_cviceni: Kvíz: odevzdání Office +nadpis_kvizu: Zkontrolujte oba soubory +--- + +# Výklad + +## Word +*Žádost* + +- zadost.docx je jedna strana A4, okraje 2,5 cm, datum 12. 6. 2026 vpravo nahoře. +- Je v ní řádek Věc, oslovení, aspoň dvě věty a podpis. Není v ní obsah ani tabulka. +- K protokolu z dřívějška, pokud ho máte, patří styly Nadpis 1 a PDF. +- PDF se jmenuje stejně jako docx. Dvě verze final a final2 se neodevzdávají. + +> Tip: Náhled tisku má ukázat jednu stránku. Podpis na druhé stránce znamená moc velké mezery. + +## Excel +*Známky* + +- znamky.xlsx má list Znamky: jméno, známka, stav. +- Stav je vzorec =KDYŽ(D2<=4;"prospěl";"neprospěl") nebo se sloupcem známky, který opravdu máte. Známka 5 je neprospěl. +- Pod tabulkou je PRŮMĚR známek, ne jejich součet. +- Čísla jsou čísla. Text 1 nebo 120 Kč ve stejné buňce se nepočítá. + +> Tip: Klik na buňku stavu musí v řádku vzorců ukázat KDYŽ. Opsané slovo prospěl se při změně známky nehne. + +## Kontrola vzorců +*Ne opis* + +- V ceníku, pokud ho přikládáte, je částka =B2*C2 a sazba ukotvená jako $F$1. +- SVYHLEDAT, pokud ho máte, končí na NEPRAVDA a oblast je ukotvená. +- Graf tržby je spojnicový, má název a není to koláč dvanácti měsíců. +- Kontingenční tabulka, pokud je v sešitu, stojí na vlastním listu a sčítá částky po třídách. + +> Tip: Stačí žádost a známky. Ceník, graf a kontingence jsou navíc, ale když je přiložíte, musí sedět. + +## Složka +*Dva hlavní soubory* + +- Ve složce Skola\1-rocnik\word je zadost.docx a zadost.pdf. +- Ve složce Skola\1-rocnik\excel je znamky.xlsx. +- Na ploše tyto soubory nejsou. Přípony jsou vidět. +- Otevřete oba znovu. Datum, věc a aspoň jedna pětka se stavem neprospěl mají být vidět. + +> Tip: Když PDF nejde otevřít, export nedoběhl. Udělejte ho z docx znovu. + +# Kvíz + +## Jaký je školní dopis z tohoto zadání? +- Tři strany se styly a obsahem +- [x] Jedna strana A4 s řádkem Věc a datem 12. 6. 2026 +- Jen tabulka 4×3 +- Prezentace +> Žádost je jeden list. Obsah a Nadpis 1 patří protokolu, ne tomuto dopisu. + +## Který vzorec označí pětku jako neprospěl? +- [x] =KDYŽ(D2<=4;"prospěl";"neprospěl") +- =KDYŽ(D2>=4;"prospěl";"neprospěl") +- napsané slovo prospěl +- =SUMA(D2) +> Prochází známka 1 až 4. Pětka padne do druhé větve. V češtině jsou středníky. + +## Co patří pod sloupec známek? +- Součet známek +- [x] Průměr +- Kontingenční součet peněz +- Nic, známky se mažou +> Součet známek nemá význam. PRŮMĚR ano. + +## Které soubory se odevzdávají jako základ? +- Jen PDF bez docx +- [x] zadost.docx, zadost.pdf a znamky.xlsx +- Jen xlsx na ploše +- prezentace a video +> Word se odevzdává i jako zdroj, i jako PDF. Excel je xlsx, protože v PDF by nebyly vzorce. diff --git a/content/programove-vybaveni/3-rocnik/01-databaze.en.md b/content/programove-vybaveni/3-rocnik/01-databaze.en.md new file mode 100644 index 0000000..e466ba7 --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/01-databaze.en.md @@ -0,0 +1,6 @@ +--- +nazev: A database +popis: A table, a record, a key. When a sheet is no longer enough. +nazev_cviceni: Quiz: databases +nadpis_kvizu: Name the key and the reason for a database +--- diff --git a/content/programove-vybaveni/3-rocnik/01-databaze.md b/content/programove-vybaveni/3-rocnik/01-databaze.md new file mode 100644 index 0000000..bcda465 --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/01-databaze.md @@ -0,0 +1,79 @@ +--- +id: sw-3-databaze +nazev: Databáze +popis: Tabulka, záznam, klíč. Kdy už nestačí Excel. +nazev_cviceni: Kvíz: databáze +nadpis_kvizu: Poznejte klíč a důvod databáze +--- + +# Výklad + +## Excel nestačí +*Vztahy* + +- Excel je výborný na jeden seznam. Jakmile se třída opakuje na každém řádku žáka, začne se název třídy rozcházet. +- Databáze drží věci v samostatných tabulkách a spojuje je klíčem. +- Tabulka tridy má název třídy jednou. Tabulka zaci se na ni jen odkáže. +- Access i MySQL umí totéž v principu. Access je soubor accdb, MySQL je server a příkazy SQL. + +> Tip: Když opravujete 1.A na 1. A v padesáti řádcích Excelu, má to být tabulka tříd, ne opis. + +## Řádek a sloupec +*Záznam a pole* + +- Řádek je záznam, třeba jeden žák. Sloupec je pole, třeba prijmeni. +- Každá hodnota je atomická. Jméno a příjmení nepatří do jedné buňky. +- Primární klíč je hodnota, která se v tabulce neopakuje. U žáka je to id, ne příjmení. +- Cizí klíč je pole, které ukazuje na primární klíč jiné tabulky. trida_id v tabulce zaci ukazuje na id v tridy. + +> Tip: Dva Nováci se smějí jmenovat stejně. Jejich id je různé. Proto příjmení klíč není. + +## Typy +*Číslo, text, datum* + +- id je číslo. Jméno je krátký text. Datum narození je datum, ne text 12.6. +- Ano/ne je samostatný typ, ne text ano. +- Do číselného pole nepatří jednotka. Počet žáků je 28, ne 28 žáků. +- Prázdné pole a nula nejsou totéž. Nula je známá hodnota, prázdno je nevíme. + +> Tip: Text v číselném poli se nepodaří uložit, nebo se sečte špatně. Typ zvolte při založení sloupce. + +## Zadání: návrh na papír +*Cvičení* + +- Navrhněte dvě tabulky: tridy (id, nazev) a zaci (id, jmeno, prijmeni, trida_id). +- U každého pole napište typ. id v obou tabulkách je primární klíč. +- Napište dva řádky tříd, 1.A a 1.B, a tři žáky, z toho Annu Novákovou ve třídě 1.A. +- Soubor navrh.txt uložte k databázím. Access ani MySQL ještě neotevírejte, dokud návrh nesedí. + +> Tip: Šipka vede od trida_id k id tabulky tridy. Opačně by žák byl klíčem třídy, což není. + +# Kvíz + +## Co je primární klíč žáka? +- Příjmení +- [x] id, které se neopakuje +- Název třídy +- První sloupec, ať je v něm cokoli +> Příjmení se opakuje. id jednoho žáka ne. + +## Co je trida_id v tabulce zaci? +- Primární klíč třídy v téže tabulce +- [x] Cizí klíč na tabulku tridy +- Text 1.A opsaný do každého řádku +- Heslo +> Cizí klíč drží jen číslo třídy. Název třídy žije v tabulce tridy jednou. + +## Kdy už je Excel na seznam žáků těsný? +- Jakmile má víc než jeden sloupec +- [x] Když se údaj, třeba název třídy, opakuje a musí zůstat stejný +- Excel nestačí nikdy +- Jakmile je soubor xlsx +> Opakovaný název se rozjede. Databáze ho má v jedné tabulce a ostatní se na ni odkážou. + +## Čím se liší nula a prázdné pole? +- Ničím +- [x] Nula je hodnota, prázdné pole znamená, že hodnota chybí +- Prázdné pole je vždy nula +- Nula je text +> Průměr nulu počítá. Prázdnou buňku často ne. Typ i význam se liší. diff --git a/content/programove-vybaveni/3-rocnik/02-access-tabulky.en.md b/content/programove-vybaveni/3-rocnik/02-access-tabulky.en.md new file mode 100644 index 0000000..3ee054f --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/02-access-tabulky.en.md @@ -0,0 +1,6 @@ +--- +nazev: Access: tables and a relationship +popis: An AutoNumber, a foreign key and a one-to-many link without a cascade. +nazev_cviceni: Quiz: Access +nadpis_kvizu: Pick the key type and the cascade +--- diff --git a/content/programove-vybaveni/3-rocnik/02-access-tabulky.md b/content/programove-vybaveni/3-rocnik/02-access-tabulky.md new file mode 100644 index 0000000..c92654e --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/02-access-tabulky.md @@ -0,0 +1,79 @@ +--- +id: sw-3-access-tabulky +nazev: Access: tabulky a vztah +popis: Automatické číslo, cizí klíč a vztah 1:N bez kaskády. +nazev_cviceni: Kvíz: Access +nadpis_kvizu: Vyberte typ klíče a kaskádu +--- + +# Výklad + +## Soubor +*accdb* + +- Databáze Accessu je jeden soubor accdb. Jmenuje se evidence.accdb. +- Tabulku založíte v návrhovém zobrazení, ne jen v datovém listu. V návrhu je vidět typ. +- Tabulka tridy má pole id a nazev. Tabulka zaci má id, jmeno, prijmeni a trida_id. +- Název pole je bez mezery a bez diakritiky: trida_id. Popisek pro lidi se dá nastavit zvlášť. + +> Tip: Datový list je na zapisování řádků. Typ pole se mění v návrhu. Když už v tabulce data jsou, změna typu může selhat. + +## Klíče +*Automatické číslo* + +- id je typ Automatické číslo a je to primární klíč. Access ho doplňuje sám: 1, 2, 3. +- trida_id je typ Číslo, dlouhé celé číslo. Není to automatické číslo. Píšete ho vy, nebo ho vybere vztah. +- nazev, jmeno a prijmeni jsou Krátký text. Délka 50 stačí. +- Dva primární klíče v jedné tabulce školního cvičení nejsou. Stačí id. + +> Tip: Když trida_id necháte jako text, vztah na číselné id třídy Access odmítne. Typy obou konců musí sedět. + +## Vztah 1:N +*Bez kaskády* + +- Jedna třída má víc žáků. Žák má jednu třídu. To je vztah 1:N. +- Ve vztazích přetáhnete tridy.id na zaci.trida_id. +- Zaškrtněte vynucení referenční integrity. Žák se nesmí odkázat na třídu, která neexistuje. +- Kaskádové odstranění nechte vypnuté. Smazání třídy by jinak smazalo i žáky. + +> Tip: Na straně tridy je jednička, na straně zaci je znak nekonečna. Když je to obráceně, táhli jste vztah špatným směrem. + +## Zadání: evidence.accdb +*Cvičení* + +- Založte evidence.accdb s tabulkami tridy a zaci podle návrhu. +- Vložte třídy 1.A a 1.B. Vložte Annu Novákovou s trida_id třídy 1.A a ještě dva žáky. +- Vztah 1:N s referenční integritou, bez kaskádového odstranění. +- Zkuste uložit žáka s trida_id 99. Access ho má odmítnout. Pak to číslo vraťte na platnou třídu. + +> Tip: Když jde uložit trida_id 99, integrita není zapnutá. Otevřete vztahy a vztah vytvořte znovu. + +# Kvíz + +## Jaký typ má id žáka? +- Krátký text, který píšete ručně +- [x] Automatické číslo a primární klíč +- Ano/ne +- Dlouhý text s celým jménem třídy +> Automatické číslo žák nedoplňuje. Access ho dá samo a v tabulce se neopakuje. + +## Jaký typ má trida_id? +- Automatické číslo +- [x] Číslo, dlouhé celé číslo +- Datum +- Ano/ne +> Je to cizí klíč na id třídy, které je taky číslo. Automatické číslo by si vymýšlelo vlastní hodnoty. + +## Co dělá referenční integrita? +- Smaže žáky při smazání třídy +- [x] Nedovolí žáka s třídou, která neexistuje +- Schová tabulku +- Převede accdb na xlsx +> Odkaz musí vést na skutečný řádek. Třída 99 bez řádku v tridy projít nemá. + +## Proč nechat kaskádové odstranění vypnuté? +- Access ho neumí +- [x] Smazání třídy by smazalo i její žáky +- Jinak nejde uložit accdb +- Kvůli Excelu +> Třída a žáci jsou různá data. Smazat třídu omylem a přijít o žáky je špatná pojistka pro školní cvičení. diff --git a/content/programove-vybaveni/3-rocnik/03-access-dotazy.en.md b/content/programove-vybaveni/3-rocnik/03-access-dotazy.en.md new file mode 100644 index 0000000..721c282 --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/03-access-dotazy.en.md @@ -0,0 +1,6 @@ +--- +nazev: Access: queries +popis: A query that shows only class 1.A, and a criterion in quotes. +nazev_cviceni: Quiz: queries +nadpis_kvizu: Fill in the criterion +--- diff --git a/content/programove-vybaveni/3-rocnik/03-access-dotazy.md b/content/programove-vybaveni/3-rocnik/03-access-dotazy.md new file mode 100644 index 0000000..679b605 --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/03-access-dotazy.md @@ -0,0 +1,79 @@ +--- +id: sw-3-access-dotazy +nazev: Access: dotazy +popis: Dotaz, který ukáže jen třídu 1.A, a kritérium v uvozovkách. +nazev_cviceni: Kvíz: dotazy +nadpis_kvizu: Doplňte kritérium +--- + +# Výklad + +## Dotaz není kopie +*Pohled* + +- Dotaz data nekopíruje do nové tabulky. Přečte je a ukáže výsledek. +- Když ve třídě přibude žák, stejný dotaz ho příště ukáže taky. +- Dotaz se dělá v návrhovém zobrazení. Tabulky se přidají a pole se přetáhnou dolů. +- Uložený dotaz se jmenuje zaci_1A. Dotaz1 se neodevzdává. + +> Tip: Když dotaz otevřete a data změníte, měníte zdrojovou tabulku. Na prohlížení to vadit nemusí, na omyl ano. + +## Kritérium +*1.A* + +- Pod pole nazev z tabulky tridy patří kritérium "1.A" včetně uvozovek. +- Bez uvozovek Access text nepozná a dotaz selže, nebo se zeptá na parametr. +- Číselné kritérium uvozovky nemá. id 1 je prostě 1. +- Chcete-li žáky obou tříd, kritérium 1.A tam nedávejte. Filtr, který tam zbyl z minula, výsledek ořeže. + +> Tip: Řádek Kritéria je filtr. Řádek Zobrazit schová sloupec, který v dotazu být musí, ale ve výsledku ho nechcete. + +## Spojení v dotazu +*Obě tabulky* + +- Do dotazu přidejte tridy i zaci. Access použije vztah, který už máte. +- Ven dejte prijmeni, jmeno a nazev třídy. id ven dávat nemusíte. +- Řazení podle prijmeni je vzestupně. V návrhové mřížce je na to řádek Řadit. +- Žák bez třídy se při vnitřním spojení neukáže. Díky integritě takový řádek stejně nemá vzniknout. + +> Tip: Když se každý žák ukáže dvakrát, tabulky nejsou spojené a Access udělal všechny kombinace. Vraťte vztah. + +## Zadání: jen 1.A +*Cvičení* + +- Dotaz zaci_1A ukáže jméno, příjmení a název třídy, jen pro 1.A, seřazené podle příjmení. +- Anna Nováková v něm je. Žák z 1.B v něm není. +- Uložte dotaz do evidence.accdb. Výsledek nekopírujte do Excelu jako jediný výstup. +- Změňte v tabulce příjmení Anny na Novák a dotaz otevřete znovu. Má ukázat Novák. + +> Tip: Když se zeptá na parametr 1.A, kritérium nemá uvozovky a Access ho bere jako otázku. + +# Kvíz + +## Kam patří podmínka "1.A"? +- Do názvu souboru +- [x] Do řádku Kritéria pod polem nazev +- Do primárního klíče místo id +- Do kaskádového odstranění +> Kritéria filtrují řádky. Pole nazev drží text 1.A. + +## Proč jsou u 1.A uvozovky? +- [x] Access jinak text pochopí jako parametr nebo chybu +- Protože 1.A je číslo +- Uvozovky se v Accessu nepoužívají +- Aby se zapnula kaskáda +> Textové kritérium se píše v uvozovkách. Číslo ne. + +## Co se stane s dotazem, když do třídy 1.A přidáte žáka? +- Dotaz je stará kopie a žáka neukáže +- [x] Po novém otevření ho ukáže +- Access databázi zavře +- Musíte ho opsat do Excelu +> Dotaz čte tabulky. Není to zmrazený export. + +## Proč se žák v dotazu objeví dvakrát u každé třídy? +- Má dva primární klíče +- [x] Tabulky v dotazu nejsou spojené vztahem +- Je seřazený podle příjmení +- Filtr je vypnutý +> Bez spojení Access zkombinuje každý řádek s každým. Vztah tomu zabrání. diff --git a/content/programove-vybaveni/3-rocnik/04-access-formulare.en.md b/content/programove-vybaveni/3-rocnik/04-access-formulare.en.md new file mode 100644 index 0000000..b3cd488 --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/04-access-formulare.en.md @@ -0,0 +1,6 @@ +--- +nazev: Access: a form and a report +popis: Entry through a form and printing through a report. The datasheet is not for users. +nazev_cviceni: Quiz: forms +nadpis_kvizu: Pick what is for entry and what is for print +--- diff --git a/content/programove-vybaveni/3-rocnik/04-access-formulare.md b/content/programove-vybaveni/3-rocnik/04-access-formulare.md new file mode 100644 index 0000000..37f9b48 --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/04-access-formulare.md @@ -0,0 +1,79 @@ +--- +id: sw-3-access-formulare +nazev: Access: formulář a sestava +popis: Zápis přes formulář a tisk přes sestavu. Datový list není pro uživatele. +nazev_cviceni: Kvíz: formulář +nadpis_kvizu: Vyberte, čím se data zadávají a čím tisknou +--- + +# Výklad + +## Formulář +*Jeden žák* + +- Formulář ukáže jeden záznam jako políčka. Datový list ukáže mřížku a tam se běžný uživatel snadno trefí do špatného řádku. +- Formulář Zaci se založí z tabulky zaci. Pole id ať je vidět, ale zamčené. Automatické číslo se nepíše. +- trida_id je lépe rozbalovací seznam, který ukazuje název třídy a ukládá id. +- Formulář se jmenuje frmZaci. Výchozí Formulář1 se neodevzdává. + +> Tip: Rozbalovací seznam bere řádky z tabulky tridy. Viditelný sloupec je nazev, vázaný sloupec je id. + +## Sestava +*Tisk* + +- Sestava je na tisk a na PDF. Formulář je na zápis. Neslouží k tomu totéž. +- Sestava žáků je seskupená podle třídy a v rámci třídy seřazená podle příjmení. +- Záhlaví skupiny ukáže 1.A jednou, ne na každém řádku jako jediný nadpis celé sestavy. +- Do sestavy nepatří tlačítka formuláře. Patří do ní nadpis Evidence žáků a číslo stránky. + +> Tip: Náhled sestavy je před tiskem povinný. Prázdná druhá stránka znamená velkou zápatí nebo prázdný oddíl. + +## Kdo kam smí +*Ne do návrhu* + +- Uživatel, který jen zapisuje žáky, dostane formulář. Návrhové zobrazení ne. +- Mazání záznamu na formuláři je vypnuté, dokud učitel neřekne jinak. +- Sestava se needituje. Když v ní přepíšete jméno, často přepisujete data, a to z sestavy dělat nechcete. +- Záloha je kopie accdb před mazáním. Jediný soubor na ploše záloha není. + +> Tip: Soubor, Uložit jako, a datum v názvu: evidence-2026-06-12.accdb. Pak teprve mažete zkušební řádky. + +## Zadání: formulář a sestava +*Cvičení* + +- frmZaci zapíše jméno, příjmení a třídu. id nejde přepsat. +- Sestava seskupí žáky podle třídy. Anna Nováková je pod 1.A. +- Z formuláře přidejte jednoho žáka do 1.B a v sestavě ho najděte. +- Uložte vše do evidence.accdb. PDF sestavy je evidence-zaci.pdf vedle souboru. + +> Tip: Když v rozbalovacím seznamu vidíte čísla místo 1.A, zobrazujete id. Přepněte šířku sloupců, ať je vidět nazev. + +# Kvíz + +## Čím se ve škole zadává nový žák? +- Přímo v návrhovém zobrazení tabulky +- [x] Formulářem frmZaci +- Přepsáním sestavy +- Excelem místo Accessu bez uložení accdb +> Formulář ukáže jeden záznam a umí seznam tříd. Návrh tabulky je pro autora databáze. + +## K čemu je sestava? +- K mazání sloupců +- [x] K tisku a PDF, seskupenému podle třídy +- K nastavení automatického čísla +- K instalaci Accessu +> Sestava formátuje výstup. Zápis zůstává formuláři. + +## Proč je id na formuláři zamčené? +- [x] Automatické číslo se nepíše ručně +- Access id neumí ukázat +- Jinak nejde sestava +- Protože je to text +> Číslo přidělí Access. Ruční přepsání by rozbilo klíč. + +## Co uděláte před mazáním zkušebních řádků? +- Nic, accdb se sám verzuje +- [x] Kopie souboru s datem v názvu +- Smažete vztah +- Export jen do JPG +> Jedna kopie accdb je záloha. Mazání bez ní nejde vzít zpět tak snadno jako Ctrl+Z ve Wordu. diff --git a/content/programove-vybaveni/3-rocnik/05-mysql-select.en.md b/content/programove-vybaveni/3-rocnik/05-mysql-select.en.md new file mode 100644 index 0000000..4e04663 --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/05-mysql-select.en.md @@ -0,0 +1,6 @@ +--- +nazev: MySQL: SELECT +popis: Choosing columns, a WHERE condition and a sort. Text sits in single quotes. +nazev_cviceni: Quiz: SELECT +nadpis_kvizu: Pick the right statement +--- diff --git a/content/programove-vybaveni/3-rocnik/05-mysql-select.md b/content/programove-vybaveni/3-rocnik/05-mysql-select.md new file mode 100644 index 0000000..2595d1b --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/05-mysql-select.md @@ -0,0 +1,79 @@ +--- +id: sw-3-mysql-select +nazev: MySQL: SELECT +popis: Výběr sloupců, podmínka WHERE a řazení. Text je v jednoduchých uvozovkách. +nazev_cviceni: Kvíz: SELECT +nadpis_kvizu: Vyberte správný příkaz +--- + +# Výklad + +## Databáze skola +*Dvě tabulky* + +- Na serveru je databáze skola. Tabulka tridy má id a nazev. Tabulka zaci má id, jmeno, prijmeni a trida_id. +- Příkazy se píšou v MySQL, ne do Excelu. Středník příkaz ukončí. +- Nejdřív USE skola; ať další příkazy nemíří do jiné databáze. +- Heslo k serveru do sešitu nepatří. Patří učiteli a do klienta, který ho neukazuje v příkazu. + +> Tip: Když klient hlásí, že tabulka neexistuje, jste v jiné databázi, nebo je v názvu překlep. SHOW TABLES; ukáže, co tam je. + +## SELECT +*Jen to, co potřebujete* + +- SELECT prijmeni, jmeno FROM zaci; vrátí dva sloupce všech žáků. +- Hvězdička SELECT * FROM zaci; je na průzkum. Do odevzdaného dotazu vyberte sloupce jménem. +- WHERE trida_id = 1 nechá jen jednu třídu. Číslo je bez uvozovek. +- Text se píše v jednoduchých uvozovkách: WHERE nazev = '1.A'. + +> Tip: Dvojité uvozovky kolem 1.A záleží na režimu serveru. Ve škole pište jednoduché, ty platí spolehlivě. + +## Pořadí +*ORDER BY* + +- ORDER BY prijmeni; seřadí vzestupně, tedy A až Ž. +- ORDER BY prijmeni DESC; je sestupně. +- WHERE patří před ORDER BY. Obrácené pořadí příkazů server odmítne. +- LIMIT 5 vrátí nejvýš pět řádků. Na kontrolu ano, na úplný seznam třídy ne. + +> Tip: Nejdřív FROM, pak WHERE, pak ORDER BY. SELECT je na začátku, i když se sloupce vybírají jako první v hlavě. + +## Zadání: tři dotazy +*Cvičení* + +- Do souboru dotazy.sql napište USE skola; a tři SELECT. +- První: příjmení a jméno všech žáků podle příjmení. +- Druhý: jen žáci s trida_id = 1. +- Třetí: název třídy 1.A z tabulky tridy. Text '1.A' je v jednoduchých uvozovkách. + +> Tip: Každý příkaz ukončete středníkem. Bez něj klient čeká na pokračování. + +# Kvíz + +## Který příkaz vrátí příjmení žáků třídy s id 1? +- [x] SELECT prijmeni FROM zaci WHERE trida_id = 1; +- SELECT prijmeni WHERE trida_id = 1; +- SELECT * FROM tridy WHERE nazev = 1.A; +- WHERE trida_id = 1; +> SELECT říká sloupce, FROM tabulku, WHERE podmínku. Číslo je bez uvozovek. + +## Jak se v podmínce zapíše název třídy? +- nazev = 1.A +- [x] nazev = '1.A' +- nazev = "bez uvozovek a bez čísla" +- nazev := 1.A +> Text patří do jednoduchých uvozovek. Bez nich to není řetězec. + +## Kam patří ORDER BY? +- Před SELECT +- [x] Za WHERE +- Místo FROM +- Do názvu tabulky +> Pořadí je WHERE, potom ORDER BY. SELECT zůstává první. + +## K čemu je ve škole SELECT * ? +- Na odevzdaný tisk všech osobních údajů +- [x] Na rychlý pohled, co v tabulce je +- Místo primárního klíče +- Na smazání řádků +> Hvězdička je průzkum. Odevzdaný dotaz jmenuje sloupce, které mají být vidět. diff --git a/content/programove-vybaveni/3-rocnik/06-mysql-zmeny.en.md b/content/programove-vybaveni/3-rocnik/06-mysql-zmeny.en.md new file mode 100644 index 0000000..a00bb9c --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/06-mysql-zmeny.en.md @@ -0,0 +1,6 @@ +--- +nazev: MySQL: INSERT, UPDATE and DELETE +popis: A new row, a change and a delete. An UPDATE or DELETE without WHERE is not written in class. +nazev_cviceni: Quiz: changing data +nadpis_kvizu: Recognise the safe statement +--- diff --git a/content/programove-vybaveni/3-rocnik/06-mysql-zmeny.md b/content/programove-vybaveni/3-rocnik/06-mysql-zmeny.md new file mode 100644 index 0000000..63fc270 --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/06-mysql-zmeny.md @@ -0,0 +1,79 @@ +--- +id: sw-3-mysql-zmeny +nazev: MySQL: INSERT, UPDATE a DELETE +popis: Nový řádek, oprava a smazání. UPDATE a DELETE bez WHERE se ve škole nepíšou. +nazev_cviceni: Kvíz: změny dat +nadpis_kvizu: Poznejte bezpečný příkaz +--- + +# Výklad + +## INSERT +*Nový žák* + +- INSERT INTO zaci (jmeno, prijmeni, trida_id) VALUES ('Anna', 'Nováková', 1); přidá jeden řádek. +- Sloupce v závorce a hodnoty za VALUES musí být ve stejném pořadí a stejném počtu. +- id s automatickým číslem do výčtu nedávejte, server ho doplní. +- Jeden INSERT, jeden žák. Balit pět žáků do nepřehledného příkazu ve cvičení nemusíte. + +> Tip: Když server hlásí neznámý sloupec, název nesedí s tabulkou. SHOW COLUMNS FROM zaci; ukáže pravdu. + +## UPDATE +*Jen jeden řádek* + +- UPDATE zaci SET trida_id = 2 WHERE id = 15; přepíše třídu jen žáku 15. +- Bez WHERE by stejný příkaz přepsat třídu všem. Ve škole se neodevzdává. +- Nejdřív SELECT s stejnou podmínkou WHERE. Až když vidíte jeden správný řádek, pusťte UPDATE. +- SET mění hodnoty. Není to SELECT a nic nevrací kromě počtu ovlivněných řádků. + +> Tip: Klient ukáže, kolik řádků se změnilo. Když vidíte desítky a čekali jste jeden, zastavte se a volejte učitele. Záloha se hodí předtím, ne potom. + +## DELETE +*Také s WHERE* + +- DELETE FROM zaci WHERE id = 15; smaže jeden řádek. +- DELETE FROM zaci; bez WHERE smaže všechny žáky. To není čištění tabulky, to je ztráta dat. +- Smazaný řádek Ctrl+Z v MySQL nevrátí. Vrátí ho záloha, nebo ho vložíte znovu, když znáte hodnoty. +- Mazat třídu, na kterou ještě ukazují žáci, referenční pravidlo nemá dovolit. Nejdřív žáci, nebo třídu nemazat. + +> Tip: Před DELETE udělejte SELECT se stejnou podmínkou. Počet řádků musí být ten, který čekáte, obvykle jeden. + +## Zadání: tři změny na kopii +*Cvičení* + +- Na cvičných datech vložte žáka Petr Svoboda do třídy 1, tedy trida_id 1. +- UPDATE mu změňte třídu na 2, ale jen přes jeho id, které jste předtím přečetli SELECTem. +- Téhož žáka DELETE smažte zase jen podle id. Ostatní žáci v tabulce zůstanou. +- Tři příkazy připojte do dotazy.sql pod komentář, který říká, že UPDATE a DELETE mají WHERE. + +> Tip: Komentář v SQL začíná dvěma pomlčkami. Do příkazu se nepočítá, příštímu čtenáři řekne, proč tam WHERE je. + +# Kvíz + +## Který příkaz přidá Annu do třídy 1? +- [x] INSERT INTO zaci (jmeno, prijmeni, trida_id) VALUES ('Anna', 'Nováková', 1); +- SELECT Anna INTO zaci; +- UPDATE zaci SET Anna; +- DELETE FROM zaci WHERE jmeno = 'Anna'; +> INSERT vkládá řádek. Sloupce a hodnoty drží pořadí. + +## Jak se změní třída jen žáku s id 15? +- UPDATE zaci SET trida_id = 2; +- [x] UPDATE zaci SET trida_id = 2 WHERE id = 15; +- SELECT trida_id = 2; +- INSERT INTO zaci VALUES (15); +> Bez WHERE by se třída přepsala všem. WHERE id = 15 drží jeden řádek. + +## Co udělá DELETE FROM zaci; bez WHERE? +- Nesmaže nic, WHERE je volitelné a server se zeptá +- [x] Smaže všechny řádky v zaci +- Smaže jen poslední řádek +- Jen vypíše žáky +> Bez podmínky je cílem celá tabulka. Ve školním cvičení takový příkaz nepište. + +## Co uděláte těsně před UPDATE? +- DELETE bez WHERE +- [x] SELECT se stejnou podmínkou, ať vidíte ty řádky +- DROP tabulky +- Vypnete integritu +> SELECT ukáže, na koho UPDATE dopadne. Až pak má smysl stejné WHERE použít ke změně. diff --git a/content/programove-vybaveni/3-rocnik/07-mysql-join.en.md b/content/programove-vybaveni/3-rocnik/07-mysql-join.en.md new file mode 100644 index 0000000..b97432f --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/07-mysql-join.en.md @@ -0,0 +1,6 @@ +--- +nazev: MySQL: joining tables +popis: An INNER JOIN on trida_id, and why the class name is not repeated on every row. +nazev_cviceni: Quiz: JOIN +nadpis_kvizu: Complete the join +--- diff --git a/content/programove-vybaveni/3-rocnik/07-mysql-join.md b/content/programove-vybaveni/3-rocnik/07-mysql-join.md new file mode 100644 index 0000000..62e5a1b --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/07-mysql-join.md @@ -0,0 +1,79 @@ +--- +id: sw-3-mysql-join +nazev: MySQL: spojení tabulek +popis: INNER JOIN přes trida_id a proč se název třídy neopakuje v každém řádku. +nazev_cviceni: Kvíz: JOIN +nadpis_kvizu: Doplňte spojení +--- + +# Výklad + +## Jedna pravda +*Název jen jednou* + +- Název 1.A je v tabulce tridy jednou. V zaci je jen trida_id. +- Když by název ležel i u žáka, oprava překlepu znamená projít všechny žáky. +- To je důvod dvou tabulek. Excel to umí jen ručně, databáze vztahem. +- JOIN tabulky spojí ve výsledku dotazu. Nesloučí je do jedné natrvalo. + +> Tip: Po JOIN pořád existují dvě tabulky. Výsledek dotazu je jen pohled pro tento příkaz. + +## INNER JOIN +*Shoda klíčů* + +- INNER JOIN vrátí řádky, kde se klíče sejdou. Žák a jeho třída. +- ON z.trida_id = t.id je podmínka spojení. Není to totéž co WHERE na název. +- Bez ON server buď příkaz odmítne, nebo spojí každý řádek s každým. +- Přezdívky z a t jsou v dotazu proto, aby bylo jasné, která tabulka je která. + +> Tip: WHERE t.nazev = '1.A' přijde až po spojení. Nejdřív se tabulky potkají, pak se filtruje. + +## Celý příkaz +*Jak má vypadat* + +- SELECT z.prijmeni, z.jmeno, t.nazev FROM zaci AS z INNER JOIN tridy AS t ON z.trida_id = t.id; +- WHERE t.nazev = '1.A' z toho udělá jen jednu třídu. +- ORDER BY z.prijmeni výsledek seřadí. +- Sloupce pište s přezdívkou tabulky. Samotné id by bylo nejasné, mají ho obě tabulky. + +> Tip: Když příkaz hlásí nejednoznačný sloupec id, napište z.id nebo t.id. Server nehádá. + +## Zadání: jeden JOIN +*Cvičení* + +- Do dotazy.sql přidejte dotaz, který vypíše příjmení, jméno a název třídy. +- Jen třída 1.A, seřazené podle příjmení. +- Anna Nováková v něm je včetně textu 1.A, ne jen čísla 1. +- Příkaz spusťte dvakrát. Výsledek má být stejný, JOIN data nemění. + +> Tip: Když vidíte číslo místo 1.A, vybíráte trida_id a ne t.nazev. Doplňte sloupec z tabulky tridy. + +# Kvíz + +## Které spojení ukáže žáka i název jeho třídy? +- SELECT * FROM zaci; +- [x] SELECT z.prijmeni, t.nazev FROM zaci AS z INNER JOIN tridy AS t ON z.trida_id = t.id; +- DELETE FROM tridy; +- INSERT INTO tridy VALUES ('1.A'); +> JOIN vezme příjmení z žáků a název z tříd tam, kde se id shoduje. + +## Co patří do ON? +- [x] z.trida_id = t.id +- t.nazev = '1.A' +- ORDER BY z.prijmeni +- USE skola +> ON je shoda klíčů. Filtr na název 1.A je WHERE, ne spojení. + +## Proč není název třídy uložený u každého žáka? +- MySQL to neumí +- [x] Aby se název měnil na jednom místě +- Protože text v MySQL nejde uložit +- Aby nešel JOIN +> Jedna pravda v tabulce tridy. Žáci drží jen číslo. + +## Co JOIN udělá s uloženými tabulkami? +- Sloučí je natrvalo do jedné +- [x] Jen složí výsledek dotazu, tabulky zůstanou dvě +- Smaže cizí klíče +- Převede je na Excel +> Výsledek je odpověď na příkaz. tridy a zaci na serveru zůstávají. diff --git a/content/programove-vybaveni/3-rocnik/08-zadani-databaze.en.md b/content/programove-vybaveni/3-rocnik/08-zadani-databaze.en.md new file mode 100644 index 0000000..b69e293 --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/08-zadani-databaze.en.md @@ -0,0 +1,6 @@ +--- +nazev: Assignment: a student register +popis: Classes, students, a relationship and one query that shows 1.A by name. +nazev_cviceni: Quiz: the register +nadpis_kvizu: Walk the keys and the query +--- diff --git a/content/programove-vybaveni/3-rocnik/08-zadani-databaze.md b/content/programove-vybaveni/3-rocnik/08-zadani-databaze.md new file mode 100644 index 0000000..d3a812b --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/08-zadani-databaze.md @@ -0,0 +1,79 @@ +--- +id: sw-3-zadani-databaze +nazev: Zadání: evidence žáků +popis: Třídy, žáci, vztah a jeden dotaz, který ukáže 1.A jménem. +nazev_cviceni: Kvíz: evidence +nadpis_kvizu: Projděte klíče i dotaz +--- + +# Výklad + +## Struktura +*Dvě tabulky* + +- tridy: id jako klíč, nazev. Řádky 1.A a 1.B. +- zaci: id jako klíč, jmeno, prijmeni, trida_id jako cizí klíč. +- Anna Nováková je ve třídě 1.A. Ve třídě 1.B je aspoň jeden jiný žák. +- Název 1.A není opsaný v tabulce žáků. Je jen v tridy. + +> Tip: Ať děláte Access, nebo MySQL, tahle dvě pole a vztah jsou povinné. Nástroj je obal. + +## Access +*Když odevzdáváte accdb* + +- evidence.accdb má vztah 1:N, referenční integritu a vypnutou kaskádu. +- Dotaz zaci_1A a formulář frmZaci. Sestava je seskupená podle třídy. +- Žák s neexistující třídou nejde uložit. Vyzkoušeno na trida_id 99. +- PDF sestavy leží vedle accdb. + +> Tip: Když učitel otevře jen accdb, formulář a dotaz v něm musí být. Samotné PDF bez souboru nestačí. + +## MySQL +*Když odevzdáváte SQL* + +- dotazy.sql obsahuje SELECT se WHERE, INSERT, UPDATE i DELETE, oba měnící příkazy s WHERE. +- JOIN vypíše příjmení a název 1.A, seřazené podle příjmení. +- V souboru není heslo k serveru a není DELETE bez WHERE. +- Příkazy jdou spustit znovu jen tam, kde to dává smysl. DELETE testovacího žáka v souboru má konkrétní id a komentář. + +> Tip: Odevzdává se text příkazů, ne snímek obrazovky klienta. Snímek smí být navíc. + +## Jedno odevzdání +*Ne obě poloviční* + +- Stačí jeden nástroj, Access nebo MySQL, ale celý: struktura, data, dotaz na 1.A. +- V dotazu je Anna vidět jako 1.A, ne jako holé číslo, pokud je součástí této části zadání JOIN nebo dotaz s názvem. +- Záloha souboru má datum v názvu, třeba evidence-2026-06-12.accdb. +- Do textu úkolu nepište heslo a nepište, že jste kaskádu zapnuli, pokud jste ji zapnout neměli. + +> Tip: Když umíte obojí, odevzdejte jedno pořádně. Dvě rozdělané databáze jsou horší než jedna hotová. + +# Kvíz + +## Kde leží text 1.A? +- V každém řádku žáka jako jediná pravda +- [x] V tabulce tridy, v poli nazev +- V názvu souboru jako jediném místě +- V hesle +> Žák má jen trida_id. Název je v tabulce tříd jednou. + +## Jaký vztah mají třídy a žáci? +- [x] 1:N, jedna třída a víc žáků +- N:N bez třetí tabulky uprostřed školního cvičení +- Žádný, stačí Excel bez klíče +- 1:1, každý žák vlastní tabulku třídy +> Jedna třída, mnoho žáků. Na straně žáků je cizí klíč. + +## Co musí mít UPDATE a DELETE v odevzdaném SQL? +- Žádnou podmínku, ať je příkaz krátký +- [x] WHERE, které trefí jen zamýšlené řádky +- Hvězdičku místo sloupců +- Kaskádu v příkazu SELECT +> Bez WHERE se mění nebo maže celá tabulka. Ve cvičení se to neodevzdává. + +## Co Access odmítne, když je integrita zapnutá? +- Žáka se třídou 1.A +- [x] Žáka s trida_id 99, které v tridy není +- Formulář +- Sestavu podle příjmení +> Cizí klíč musí ukazovat na existující třídu. 99 v tabulce tříd není. diff --git a/content/programove-vybaveni/4-rocnik/01-wordpress.en.md b/content/programove-vybaveni/4-rocnik/01-wordpress.en.md new file mode 100644 index 0000000..d8adbc0 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/01-wordpress.en.md @@ -0,0 +1,6 @@ +--- +nazev: WordPress: pages and posts +popis: A school site, a static page and a dated news item. The system is not hand-written HTML for every sentence. +nazev_cviceni: Quiz: WordPress +nadpis_kvizu: Tell a page from a post +--- diff --git a/content/programove-vybaveni/4-rocnik/01-wordpress.md b/content/programove-vybaveni/4-rocnik/01-wordpress.md new file mode 100644 index 0000000..de527c6 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/01-wordpress.md @@ -0,0 +1,79 @@ +--- +id: sw-4-wordpress +nazev: WordPress: stránky a příspěvky +popis: Web školy, statická stránka a datovaná zpráva. Systém není ručně psané HTML každé věty. +nazev_cviceni: Kvíz: WordPress +nadpis_kvizu: Rozlište stránku a příspěvek +--- + +# Výklad + +## Co to je +*Správa obsahu* + +- WordPress je systém pro web. Text se píše v administraci, ne do každé stránky jako do samostatného souboru ve Wordu. +- Školní web se jmenuje Školní den. Název se nastaví v Nastavení, Obecné. +- Administrace je cesta /wp-admin/ na adrese webu. Bez účtu tam nejste. +- Veřejná stránka a administrace jsou dva pohledy. Návštěvník administraci nevidí. + +> Tip: Když se po uložení na webu nic nezměnilo, často jste jen uložili koncept. Zveřejnit je samostatný krok. + +## Stránka a příspěvek +*Dva druhy* + +- Stránka je trvalá: O škole, Kontakt. Nemá datum jako hlavní údaj. +- Příspěvek je zpráva: co se stalo 12. 6. 2026. Řadí se od nejnovějšího. +- Školní den má aspoň jednu stránku O škole a jeden příspěvek o dni otevřených dveří. +- Příspěvek není stránka s jiným názvem. V menu se s nimi pracuje jinak. + +> Tip: V administraci jsou Příspěvky a Stránky dvě samostatné položky. Když novinku přidáte jako stránku, na výpisu zpráv nebude. + +## Adresa +*Trvalý odkaz* + +- Trvalý odkaz v Nastavení, Trvalé odkazy, ať je název příspěvku, ne ?p=123. +- Adresa pak nese slova, třeba /den-otevrenych-dveri/. +- Jednou zveřejněnou adresu neměňte jen kvůli stylu. Odkaz, který už někdo má, by se rozbil. +- Název webu a adresa příspěvku jsou dvě věci. Název je Školní den, adresa je konkrétní článek. + +> Tip: Když škola už odkazy rozeslala, změnu trvalých odkazů nechte na učiteli. Cvičný web si klidně nastavte hned na začátku. + +## Zadání: dvě věci na webu +*Cvičení* + +- Na cvičném webu nastavte název Školní den. +- Zveřejněte stránku O škole, aspoň dvě věty, bez data jako hlavní osy. +- Zveřejněte příspěvek Den otevřených dveří s datem 12. 6. 2026. +- Do souboru web.txt napište adresu stránky a adresu příspěvku. Heslo do souboru nepište. + +> Tip: Koncept se na veřejné stránce neukáže. U obou má být stav Publikováno. + +# Kvíz + +## Čím se liší stránka od příspěvku? +- Ničím, je to stejné tlačítko +- [x] Stránka je trvalá, příspěvek je datovaná zpráva +- Příspěvek nejde zveřejnit +- Stránka musí být v Excelu +> O škole je stránka. Novinka s datem je příspěvek. + +## Kam se nastaví název webu Školní den? +- Do názvu počítače +- [x] Do Nastavení, Obecné +- Do buňky A1 +- Do příkazu DELETE +> Název webu je nastavení WordPressu, ne název souboru Windows. + +## Jaký trvalý odkaz ve cvičení chcete? +- ?p=123 natrvalo +- [x] Název příspěvku ve adrese +- Jen číslo id žáka +- Adresu z Accessu +> Adresa se slovy se čte a pamatuje. Číselný parametr ne. + +## Co napsat do web.txt? +- Heslo správce +- [x] Adresy zveřejněné stránky a příspěvku +- Celou databázi MySQL +- Soubor accdb +> Úkol dokládá, že stránky jsou venku. Heslo do textového souboru nepatří. diff --git a/content/programove-vybaveni/4-rocnik/02-wordpress-role.en.md b/content/programove-vybaveni/4-rocnik/02-wordpress-role.en.md new file mode 100644 index 0000000..bee86a9 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/02-wordpress-role.en.md @@ -0,0 +1,6 @@ +--- +nazev: WordPress: users and appearance +popis: An author writes their own news, an editor also edits others. There is one administrator. +nazev_cviceni: Quiz: roles +nadpis_kvizu: Match the role +--- diff --git a/content/programove-vybaveni/4-rocnik/02-wordpress-role.md b/content/programove-vybaveni/4-rocnik/02-wordpress-role.md new file mode 100644 index 0000000..a4d5f49 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/02-wordpress-role.md @@ -0,0 +1,79 @@ +--- +id: sw-4-wordpress-role +nazev: WordPress: uživatelé a vzhled +popis: Autor píše své zprávy, šéfredaktor i cizí. Správce je jeden. +nazev_cviceni: Kvíz: role +nadpis_kvizu: Přiřaďte roli +--- + +# Výklad + +## Role +*Nejmíň práv* + +- Správce instaluje doplňky, mění šablonu a zakládá uživatele. Ve třídě je jeden, účet učitele. +- Šéfredaktor zveřejňuje a upravuje i cizí příspěvky a stránky. Správce to být nemusí. +- Autor zveřejňuje vlastní příspěvky. Cizí ne. Stránky O škole nezakládá. +- Spolužák, který jen přidává novinky, dostane roli Autor. Ne správce. + +> Tip: Přihlášení každého člověka je vlastní účet. Heslo se nepůjčuje, ani když jde o školní cvičení. + +## Šablona a doplňky +*Málo* + +- Šablona je vzhled. Ve cvičení stačí jedna, třeba výchozí, kterou učitel určí. +- Doplněk přidává funkci. Každý doplněk je další věc, která se musí aktualizovat. +- Doplněk z neznámého webu se neinstaluje. Stačí jeden překlep a web běží s cizím kódem. +- Aktualizace WordPressu, šablony a doplňků se dělají v klidný den a po záloze, ne minutu před dnem otevřených dveří. + +> Tip: Neaktivní doplněk, který nepoužíváte, smažte. Vypnutý pořád leží na disku a pořád se může hodit útočníkovi, když v něm je chyba. + +## Menu +*Co návštěvník vidí* + +- Menu má položku O škole na stránku, ne na koncept. +- Příspěvky se často vypisují samy na úvodu. Do menu se nemusí přidávat každý. +- Zkouškový příspěvek s textem test test do veřejného menu nepatří. Smažte ho, nebo ho nechte jako koncept. +- Název v menu a nadpis stránky ať říkají totéž. O škole a O nas jsou dvě stránky, ne jedna. + +> Tip: Náhled v anonymním okně ukáže web bez vašeho přihlášení. To je pohled návštěvníka. + +## Zadání: účet autora +*Cvičení* + +- Založte uživatele redakce s rolí Autor. Heslo mu nastavte mimo sešit, do web.txt ho nepište. +- Ověřte, že tento účet neumí instalovat doplňky. Když umí, je to správce a roli změňte. +- Šablonu neměňte na náhodnou z internetu. Nechte tu, kterou web už má, nebo tu od učitele. +- Do web.txt dopište přihlašovací jméno autora a že role je Autor. Ne heslo. + +> Tip: Uživatelé, Všichni uživatelé, sloupec Role. Autor tam musí být napsaný, nestačí, že to tak bylo v úmyslu. + +# Kvíz + +## Jakou roli dostane spolužák, který jen píše novinky? +- Správce +- [x] Autor +- Jediný účet s vaším heslem +- Roli MySQL +> Autor zveřejní vlastní příspěvek a nesahá na doplňky ani na cizí texty. + +## Kdo smí instalovat doplňky? +- Každý návštěvník +- [x] Správce +- Autor +- Kdokoli s odkazem na článek +> Doplňky mění celý web. To je práce správce, ne autora zprávy. + +## Proč se doplněk nestahuje z náhodného webu? +- WordPress doplňky neumí +- [x] Je to cizí kód, který běží na školním webu +- Protože pak nejde napsat stránku +- Doplňky jsou vždycky v Excelu +> Doplněk je program. Neznámý zdroj do školního webu nepatří. + +## Co do web.txt o účtu redakce nepatří? +- Přihlašovací jméno +- Role Autor +- [x] Heslo +- To, že účet nezvládne instalovat doplňky +> Heslo do textového úkolu nepište. Jméno a role ano. diff --git a/content/programove-vybaveni/4-rocnik/03-power-bi.en.md b/content/programove-vybaveni/4-rocnik/03-power-bi.en.md new file mode 100644 index 0000000..fb41b75 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/03-power-bi.en.md @@ -0,0 +1,6 @@ +--- +nazev: Power BI: data from Excel +popis: One workbook, one table, a chart by class. The same data is not loaded twice. +nazev_cviceni: Quiz: Power BI +nadpis_kvizu: Pick the source and the visual +--- diff --git a/content/programove-vybaveni/4-rocnik/03-power-bi.md b/content/programove-vybaveni/4-rocnik/03-power-bi.md new file mode 100644 index 0000000..e3c9d21 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/03-power-bi.md @@ -0,0 +1,79 @@ +--- +id: sw-4-power-bi +nazev: Power BI: data z Excelu +popis: Jeden sešit, jedna tabulka, graf podle třídy. Stejná data se nenačítají dvakrát. +nazev_cviceni: Kvíz: Power BI +nadpis_kvizu: Vyberte zdroj a vizuál +--- + +# Výklad + +## Soubor +*pbix* + +- Power BI Desktop ukládá práci do pbix. Není to xlsx a není to obrázek grafu. +- Na odevzdání vedle pbix patří PDF stránky sestavy, ať ji otevře i člověk bez Power BI. +- Zdroj je prodej.xlsx, tabulka Prodej se sloupci Polozka, Trida, Castka. +- Načtení je Načíst data, Excel, a vybere se tabulka Prodej, ne celý sešit naslepo dvakrát. + +> Tip: Když v modelu vidíte Prodej a Prodej (2), tabulka se načetla dvakrát. Jednu odeberte, než postavíte graf. + +## Model +*Jedna řádka, jedna skutečnost* + +- Každý řádek je jeden prodej: položka, třída, částka. Součet v tomtéž sloupci navíc řádek souhrnu být nemá. +- Řádek souhrnů z Excelu do Power BI nepatří. Součet si spočítá vizuál. +- Trida je text 1.A a 1.B. Castka je číslo bez Kč v buňce. +- Když Excel opravíte, v Power BI dejte Obnovit. Sám se o uložení xlsx nedozví. + +> Tip: Sloupec, který Power BI bere jako text, se nesčítá. Často je v něm mezera nebo Kč. V Excelu ho opravte a obnovte. + +## Vizuál +*Sloupec a karta* + +- Sloupcový graf: osa Trida, hodnota součet Castka. Porovnání dvou tříd. +- Karta ukáže jedno číslo, celkový součet Castka. +- Koláč pro dvě třídy je zbytečný. Pro dvanáct měsíců byl špatně už v Excelu. +- Průřez, anglicky slicer, na pole Trida nechte na příští lekci. Teď ať graf ukáže obě třídy. + +> Tip: Když graf ukáže počet řádků místo součtu korun, v hodnotách je Počet. Přepněte souhrn na Součet. + +## Zadání: první pbix +*Cvičení* + +- prodej.pbix načte tabulku Prodej jednou. +- Na stránce jsou sloupcový graf podle třídy a karta s celkovou částkou. +- Obnovte data potom, co v Excelu změníte jednu částku. Karta se musí pohnout. +- Uložte pbix do složky Skola\1-rocnik\excel a vedle něj prodej.pdf. + +> Tip: PDF je snímek sestavy. pbix je práce, ve které jde vizuál opravit. Odevzdávají se oba. + +# Kvíz + +## Jaký soubor je pracovní v Power BI? +- xlsx jako jediný +- [x] pbix +- pdf jako jediný +- accdb +> pbix drží model a vizuály. Excel je zdroj. PDF je náhled na odevzdání. + +## Jak se mají načíst data prodeje? +- [x] Tabulka Prodej jednou +- Tentýž list dvakrát +- Jako obrázek grafu z Excelu +- Přepisem do Wordu +> Jedna tabulka v modelu. Druhá kopie téhož zdroje mate součty. + +## Jaký vizuál porovná částku tříd 1.A a 1.B? +- Koláč s dvanácti díly +- [x] Sloupcový graf, osa Trida, součet Castka +- Karta bez popisku a bez čísla +- Průřez místo grafu jako jediný prvek +> Dvě kategorie a jejich součet jsou sloupce. Karta je jen jedno číslo celkem. + +## Co uděláte v Power BI potom, co změníte číslo v Excelu? +- Nic, pbix se mění sám v tu vteřinu +- [x] Obnovit +- Smazat pbix a začít ve Wordu +- Převést Castka na text +> Power BI si data při načtení vezme. Další změna Excelu se projeví až po obnovení. diff --git a/content/programove-vybaveni/4-rocnik/04-power-bi-prurez.en.md b/content/programove-vybaveni/4-rocnik/04-power-bi-prurez.en.md new file mode 100644 index 0000000..29027c6 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/04-power-bi-prurez.en.md @@ -0,0 +1,6 @@ +--- +nazev: Power BI: a slicer and a report +popis: A class slicer filters the whole page. A filter on one visual leaves the others. +nazev_cviceni: Quiz: the slicer +nadpis_kvizu: Say what the slicer affects +--- diff --git a/content/programove-vybaveni/4-rocnik/04-power-bi-prurez.md b/content/programove-vybaveni/4-rocnik/04-power-bi-prurez.md new file mode 100644 index 0000000..c2e7f08 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/04-power-bi-prurez.md @@ -0,0 +1,79 @@ +--- +id: sw-4-power-bi-prurez +nazev: Power BI: průřez a sestava +popis: Průřez třídy přefiltruje celou stránku. Filtr jednoho vizuálu ostatní nechá. +nazev_cviceni: Kvíz: průřez +nadpis_kvizu: Řekněte, co průřez ovlivní +--- + +# Výklad + +## Průřez +*Celá stránka* + +- Průřez na pole Trida je ovládací prvek na plátně. Čtenář klikne na 1.A. +- Sloupcový graf i karta se mají změnit. Celková částka pak není celá škola, ale jen 1.A. +- Když se karta nehne, průřez na ni není napojený, nebo karta bere jinou tabulku. +- Výběr se zruší gumou na průřezu, nebo volbou všech tříd. Data se tím nesmažou. + +> Tip: Průřez filtruje. Není to nový součet uložený do Excelu. + +## Filtr vizuálu +*Jen jeden graf* + +- Filtr v podokně konkrétního vizuálu ovlivní jen ten vizuál. +- Školní sestava pro porovnání tříd filtr vizuálu nepotřebuje. Má ukázat obě, dokud čtenář nezvolí průřez. +- Filtr stránky schová třídu všem vizuálům na stránce, i bez klikání. Na odevzdání ho nenechávejte zapnutý omylem. +- Než sestavu vyexportujete, zrušte výběr průřezu, pokud má PDF ukázat celou školu. + +> Tip: PDF zachytí průřez tak, jak je zrovna vybraný. Vybraná 1.A v PDF už druhou třídu neukáže. + +## Stránka +*Jedna otázka* + +- Stránka se jmenuje Prodej. Název Stránka 1 se neodevzdává. +- Nadpis na plátně říká Prodej po třídách. Graf má popisky tříd. +- Na jedné stránce jsou nejvýš průřez, jeden graf a jedna karta. Druhý graf se stejným obsahem je navíc. +- Barvy nechte klidné. Červená a zelená těsně vedle sebe se špatně čte. + +> Tip: Zobrazte sestavu na projektoru, nebo aspoň v celém okně. Drobný popisek, který na vašem monitoru ujde, vzadu ve třídě zanikne. + +## Zadání: stránka s průřezem +*Cvičení* + +- Na stránku Prodej přidejte průřez Trida. +- Klik na 1.A změní graf i kartu. Po zrušení výběru je zase vidět celý součet. +- PDF vyexportujte bez vybrané třídy, ať ukazuje 1.A i 1.B. +- pbix uložte znovu. V modelu je tabulka Prodej pořád jen jednou. + +> Tip: Když průřez nabízí prázdný řádek, v Excelu je prázdný řádek tabulky. Smažte ho ve zdroji a obnovte. + +# Kvíz + +## Co má průřez na Trida změnit? +- Jen barvu pozadí +- [x] Graf i kartu na stránce +- Soubor xlsx na disku +- Heslo k WordPressu +> Průřez filtruje vizuály na stránce. Karta s celkovou částkou se musí změnit taky. + +## Čím se liší filtr jednoho vizuálu od průřezu? +- Ničím +- [x] Filtr vizuálu platí jen pro něj, průřez je ovládání pro stránku +- Filtr vizuálu smaže Excel +- Průřez nejde zrušit +> Průřez je na plátně pro čtenáře. Filtr v podokně vizuálu ostatní grafy nechá. + +## Jaké PDF se odevzdává, když mají být vidět obě třídy? +- S vybranou jen 1.A +- [x] Bez výběru v průřezu +- Prázdná stránka +- Jen karta bez grafu a bez tříd +> PDF drží aktuální výběr. Než ho uděláte, výběr zrušte. + +## Jak se má stránka jmenovat? +- Stránka 1 +- [x] Prodej +- pbix +- DELETE +> Název říká, co na stránce je. Výchozí Stránka 1 se neodevzdává. diff --git a/content/programove-vybaveni/4-rocnik/05-batch.en.md b/content/programove-vybaveni/4-rocnik/05-batch.en.md new file mode 100644 index 0000000..bbfeb4d --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/05-batch.en.md @@ -0,0 +1,6 @@ +--- +nazev: Batch files +popis: A bat file that creates the school folders and deletes nothing. +nazev_cviceni: Quiz: batch +nadpis_kvizu: Pick the safe command +--- diff --git a/content/programove-vybaveni/4-rocnik/05-batch.md b/content/programove-vybaveni/4-rocnik/05-batch.md new file mode 100644 index 0000000..b110c71 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/05-batch.md @@ -0,0 +1,79 @@ +--- +id: sw-4-batch +nazev: Dávkové soubory +popis: Soubor bat, který založí složky školy a nic nemaže. +nazev_cviceni: Kvíz: batch +nadpis_kvizu: Vyberte bezpečný příkaz +--- + +# Výklad + +## Soubor bat +*Příkazy za sebou* + +- Dávkový soubor je text s příponou bat. Příkazový řádek ho spustí řádek po řádku. +- První řádek cvičení je @echo off, ať se příkazy nevypisují podruhé do okna. +- rem je komentář. Do příkazu se nepočítá. +- Soubor se jmenuje slozky.bat a leží v Dokumentech, ne na cizím flash disku, kterému nerozumíte. + +> Tip: Dvojklik spustí bat v té složce, kde soubor je. Neznámý bat z mailu se nespouští, stejně jako pdf.exe. + +## Složky +*mkdir* + +- mkdir založí složku. Když už existuje, příkaz ohlásí chybu a pokračuje se dál, složku nesmaže. +- set "ROOT=%USERPROFILE%\Documents\Skola" uloží cestu. %USERPROFILE% je složka účtu, u každého jiná. +- mkdir "%ROOT%\1-rocnik\word" založí i mezistupně, když je napíšete takto v nových Windows. Když ne, založte nejdřív 1-rocnik a pak word. +- Uvozovky kolem cesty jsou povinné v okamžiku, kdy v cestě je mezera. V Documents mezera není, v názvu účtu být může. + +> Tip: echo Hotovo vypíše větu. pause nechá okno otevřené, ať výsledek přečtete. Bez pause dvojklik okno hned zavře. + +## Co se nepouští +*del* + +- del smaže soubor. Ve školním bat na založení složek žádný del není. +- Příkaz, který maže podle masky celý disk, se nepíše ani z legrace. Obnova není Ctrl+Z. +- cd /d přejde na jiný disk a složku. Než něco mažete, echo vypíše, kde jste. V tomto cvičení nemažete. +- Bat, kterému nerozumíte řádek po řádku, nespouštějte. Krátký soubor se čte celý. + +> Tip: Když chcete zkusit del, zkuste ho na jeden soubor test.txt, který jste sami právě vytvořili. Ne na složku Windows. + +## Zadání: slozky.bat +*Cvičení* + +- slozky.bat nastaví ROOT na %USERPROFILE%\Documents\Skola a založí 1-rocnik\word, 1-rocnik\excel a 1-rocnik\system. +- Na konci je echo s cestou a pause. Není tam del. +- Spusťte ho jednou. Složky v Průzkumníku opravdu jsou. +- Spusťte ho podruhé. Smí ohlásit, že složka už existuje. Nemá nic smazat. + +> Tip: Úprava bat je v Poznámkovém bloku, ne ve Wordu. Word do něj doplní formátování a příkazový řádek pak řádek nepochopí. + +# Kvíz + +## Co má být první řádek školního bat? +- del C:\ +- [x] @echo off +- formát disku +- heslo účtu +> @echo off jen ztiší opisování příkazů. Nic nemaže. + +## Co dělá mkdir? +- Smaže složku +- [x] Založí složku +- Otevře Word +- Změní heslo +> mkdir je make directory. Mazání je jiný příkaz a v tomto cvičení není. + +## Kam ukazuje %USERPROFILE%? +- Na stejnou složku na všech účtech počítače +- [x] Na profil přihlášeného účtu +- Na web WordPressu +- Na tabulku tridy +> Každý účet má vlastní profil. Stejný bat tedy založí Skola v Dokumentech toho, kdo ho spustil. + +## Který bat ve cvičení nespouštíte? +- Ten, který jen zakládá složky a na konci čeká +- [x] Neznámý soubor z mailu +- slozky.bat, který jste sami přečetli +- Soubor, který ohlásí, že složka už existuje +> Dávka je program. Z mailu se nespouští, stejně jako soubor s příponou exe. diff --git a/content/programove-vybaveni/4-rocnik/06-uzivatele-a-skupiny.en.md b/content/programove-vybaveni/4-rocnik/06-uzivatele-a-skupiny.en.md new file mode 100644 index 0000000..840ca87 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/06-uzivatele-a-skupiny.en.md @@ -0,0 +1,6 @@ +--- +nazev: Users and groups in Windows +popis: The zaci account is in Users. It is not in Administrators. +nazev_cviceni: Quiz: groups +nadpis_kvizu: Put the account in the right group +--- diff --git a/content/programove-vybaveni/4-rocnik/06-uzivatele-a-skupiny.md b/content/programove-vybaveni/4-rocnik/06-uzivatele-a-skupiny.md new file mode 100644 index 0000000..75a3c74 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/06-uzivatele-a-skupiny.md @@ -0,0 +1,79 @@ +--- +id: sw-4-uzivatele +nazev: Uživatelé a skupiny ve Windows +popis: Účet zaci je ve Users. Ve skupině Administrators není. +nazev_cviceni: Kvíz: skupiny +nadpis_kvizu: Zařaďte účet do skupiny +--- + +# Výklad + +## Skupina +*Práva najednou* + +- Skupina sdružuje účty, které mají mít stejná práva. Neměníte právo každému účtu zvlášť po jednom souboru. +- Users jsou běžní uživatelé. Administrators smí měnit systém a instalovat. +- Účet zaci patří do Users. Do Administrators ne. +- Nový účet je často v Users sám. Práce navíc je nestrkat ho mezi správce, ne dopisovat Users. + +> Tip: Kdo je správce, vypíše net localgroup Administrators. Jméno zaci v tom seznamu být nemá. + +## Příkazy +*Bez hesla v řádku* + +- net user zaci * /add účet založí a na heslo se zeptá. Hvězdička znamená dotaz, ne heslo zapsané do bat. +- net user zaci vypíše účet. Je tam vidět, jestli je aktivní. +- net localgroup Users zaci /add ho přidá do Users, kdyby tam nebyl. +- net localgroup Administrators zaci /delete ho ze správců odebere, pokud tam omylem je. Příkaz chce spustit správce. + +> Tip: lusrmgr.msc otevře stejné účty v okně, ale jen ve Windows Pro, Education a Enterprise. V edici Home toto okno není a zůstává net user. + +## Kdo příkaz spouští +*Správce* + +- Běžný uživatel účet nezaloží. Okno se musí spustit jako správce, nebo systém požádá o souhlas. +- Souhlas správce není důvod založit účet rovnou jako správce. zaci zůstává běžný. +- whoami na účtu zaci nesmí skončit na Administrators. +- Cvičný účet se po hodině dá odebrat: net user zaci /delete. Nejdřív si ověřte, že nemaže účet učitele. + +> Tip: Jméno účtu zaci je cvičné. Účet, který se jmenuje jako učitel nebo správce, se nedeaktivuje a nemaže. + +## Zadání: zápis, ne cizí účty +*Cvičení* + +- Do ucty.txt napište příkaz, kterým se zaci založí, včetně hvězdičky místo hesla. +- Napište, v které skupině má být a ve které být nemá. +- Pokud učitel dovolí, účet opravdu založte a net localgroup Administrators opište. zaci v seznamu není. +- Heslo do ucty.txt nepište. Cizí účty na počítači neměňte. + +> Tip: Když lusrmgr.msc hlásí, že neexistuje, máte edici Home. Použijte net user a do zápisu napište, že grafické okno v této edici není. + +# Kvíz + +## Do které skupiny patří zaci? +- Administrators +- [x] Users +- Do žádné, účet bez skupiny smí instalovat +- Do skupiny hesel +> Běžná práce je skupina Users. Správci jsou Administrators. + +## Jak se účet založí, aniž by heslo zůstalo v příkazu? +- [x] net user zaci * /add +- net user zaci heslo123 /add +- mkdir zaci +- DELETE FROM zaci +> Hvězdička se zeptá. Heslo napsané v příkazu zůstane v historii. + +## Kde lusrmgr.msc není? +- Ve Windows Pro +- [x] Ve Windows Home +- Ve Windows Education +- Ve Windows Enterprise +> Grafická správa účtů je v Pro, Education a Enterprise. Home ji nemá. + +## Co whoami nesmí ukázat při denní práci na účtu zaci? +- Že jde o běžného uživatele +- [x] Že účet je ve skupině Administrators +- Název počítače +- To, že účet existuje +> Denní účet není správce. whoami to prozradí dřív, než něco nainstalujete. diff --git a/content/programove-vybaveni/4-rocnik/07-zasady.en.md b/content/programove-vybaveni/4-rocnik/07-zasady.en.md new file mode 100644 index 0000000..a14b512 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/07-zasady.en.md @@ -0,0 +1,6 @@ +--- +nazev: Local policy +popis: A minimum password of 8 characters and a lock after 300 seconds. A school domain overrides the local policy. +nazev_cviceni: Quiz: policy +nadpis_kvizu: Fill in the value and the command +--- diff --git a/content/programove-vybaveni/4-rocnik/07-zasady.md b/content/programove-vybaveni/4-rocnik/07-zasady.md new file mode 100644 index 0000000..f850445 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/07-zasady.md @@ -0,0 +1,79 @@ +--- +id: sw-4-zasady +nazev: Místní zásady +popis: Minimální heslo 8 znaků a zámek po 300 sekundách. Doména školy místní zásadu přebije. +nazev_cviceni: Kvíz: zásady +nadpis_kvizu: Doplňte hodnotu a příkaz +--- + +# Výklad + +## Kde se zásady berou +*gpedit* + +- Místní zásady otevře gpedit.msc. Je ve Windows Pro, Education a Enterprise, v edici Home není. +- Konfigurace počítače platí pro stroj. Konfigurace uživatele platí pro účty, které se přihlásí. +- Školní cvičení mění dvě věci: délku hesla a zámek při nečinnosti. Ostatní zásady nechte být. +- Když je počítač v doméně školy, doménová zásada vyhraje nad místní. Místní změna se pak jakoby ztratí a není to chyba klikání. + +> Tip: Než něco změníte, napište si původní hodnotu. Vrátit Neznámé číslo zpět nejde, když jste si ho nezapsali. + +## Heslo +*Osm znaků* + +- Cesta vede přes Konfigurace počítače, Nastavení systému Windows, Nastavení zabezpečení, Zásady účtů, Zásady hesel. +- Minimální délka hesla se nastaví na 8. +- Nula znamená, že heslo smí být prázdné. To školní účet nechce. +- Zásada neopraví heslo, které už je krátké, dokud ho uživatel příště nemění. Platí pro nová hesla. + +> Tip: Heslo v zásadě nenastavujete. Nastavujete pravidlo. Samotné heslo účtu zaci se zadává při net user, ne tady. + +## Zámek +*300 sekund* + +- Nečinnost: Konfigurace počítače, Nastavení systému Windows, Nastavení zabezpečení, Místní zásady, Možnosti zabezpečení. +- Položka Interaktivní přihlášení: limit nečinnosti počítače se nastaví na 300 sekund, tedy 5 minut. +- Nula nebo nenastaveno znamená, že tato zásada zámek nevynucuje. +- Po pěti minutách bez myši a klávesnice se má počítač zamknout, ne vypnout a ne smazat Dokumenty. + +> Tip: Zámek je Win+L hned. Zásada totéž udělá sama, když odejdete od stroje a zapomenete. + +## Zadání: dvě hodnoty +*Cvičení* + +- Pokud gpedit.msc máte, nastavte minimální délku hesla na 8 a limit nečinnosti na 300 sekund. Původní hodnoty si zapište. +- gpupdate /force zásady načte hned, bez čekání na restart. Příkaz chce okno spuštěné jako správce. +- Do zasady.txt napište obě hodnoty a jestli je počítač v doméně. Heslo nepište. +- V edici Home napište, že gpedit.msc v ní není, a hodnoty jen popište. Nevymýšlejte, že jste je nastavili. + +> Tip: Když po gpupdate hodnota v gpedit zůstane a systém se nechová podle ní, počítač je v doméně a doména místní číslo přebíjí. + +# Kvíz + +## Jaká je školní minimální délka hesla? +- 0 +- [x] 8 +- 300 +- Prázdné heslo +> Osm znaků je pravidlo z první hodiny o účtech. Nula by prázdné heslo dovolila. + +## Na kolik sekund se nastaví limit nečinnosti? +- 5 +- [x] 300 +- 8 +- Na nulu, ať se počítač nezamyká +> 300 sekund je pět minut. Číslo 5 by bylo pět sekund, to v této položce není. + +## Čím se místní zásady načtou hned? +- Smazáním účtu zaci +- [x] gpupdate /force +- mkdir +- SELECT * FROM zaci +> gpupdate zásady znovu načte. Bez něj si počítač může počkat na své vlastní obnovení. + +## Co platí, když je počítač v doméně školy a místní heslo nastavíte jinak? +- Vždy vyhraje místní gpedit +- [x] Vyhraje doménová zásada +- Hesla se vypnou +- WordPress přestane běžet +> Doména je nad místním počítačem. Místní změna, která jí odporuje, se neuplatní. diff --git a/content/programove-vybaveni/4-rocnik/08-maturitni-zadani.en.md b/content/programove-vybaveni/4-rocnik/08-maturitni-zadani.en.md new file mode 100644 index 0000000..fdca7fe --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/08-maturitni-zadani.en.md @@ -0,0 +1,6 @@ +--- +nazev: Exam assignment +popis: Folders, a letter, grades, a register, a site, a report and an account with a password rule. +nazev_cviceni: Quiz: the whole assignment +nadpis_kvizu: Walk the hand-in +--- diff --git a/content/programove-vybaveni/4-rocnik/08-maturitni-zadani.md b/content/programove-vybaveni/4-rocnik/08-maturitni-zadani.md new file mode 100644 index 0000000..ef404a3 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/08-maturitni-zadani.md @@ -0,0 +1,79 @@ +--- +id: sw-4-maturita +nazev: Maturitní zadání +popis: Složky, dopis, známky, evidence, web, sestava a účet s zásadou hesla. +nazev_cviceni: Kvíz: celé zadání +nadpis_kvizu: Projdi odevzdání +--- + +# Výklad + +## Systém +*Složky a účet* + +- slozky.bat založí Skola\1-rocnik\word, excel a system v Dokumentech toho účtu, který ho spustí. +- V bat není del a není v něm heslo. Je v něm @echo off, mkdir a pause. +- Účet zaci je běžný uživatel ve skupině Users, není v Administrators. +- Minimální délka hesla je 8 znaků. Zámek po nečinnosti je 300 sekund, pokud edice Windows zásady má. + +> Tip: Do textového souboru patří příkazy a hodnoty. Skutečné heslo ne. + +## Office +*Dopis a známky* + +- zadost.docx je jedna strana, datum 12. 6. 2026, řádek Věc, k tomu PDF. +- znamky.xlsx počítá stav vzorcem KDYŽ. Známka 5 je neprospěl, 1 až 4 prospěl. +- Pod známkami je průměr, ne součet. +- prodej.xlsx a prodej.pbix: tabulka načtená jednou, průřez třídy, PDF bez výběru, ať jsou vidět obě třídy. + +> Tip: pbix bez xlsx zdroje učitel neobnoví. Xlsx vedle pbix nechte. + +## Data a web +*Evidence a Školní den* + +- Access nebo MySQL: tabulky tridy a zaci, vztah 1:N, Anna Nováková ve třídě 1.A. +- Dotaz ukáže název 1.A, ne jen číslo. UPDATE a DELETE, pokud jsou v SQL, mají WHERE. +- WordPress: název Školní den, stránka O škole, příspěvek s datem, účet autora. Správce je jeden. +- V textech o webu a účtech není heslo. + +> Tip: Stačí jeden databázový nástroj, ale celý. Access s integritou, nebo SQL s JOINem. + +## Složka odevzdání +*Pořádek* + +- Všechno leží pod Skola, ne na ploše a ne v Stažených. +- Přípony jsou vidět. Názvy nemají final2. +- Na obhajobu otevřete nejdřív bat a složky, pak Excel a Power BI, pak databázi a nakonec web. +- U každé části umíte říct, kdo smí heslo, kdo smí instalovat a proč pětka není prospěl. + +> Tip: Když se zeptají, proč zaci není správce, odpověď je: denní účet nemá právo měnit systém. + +# Kvíz + +## Co slozky.bat nesmí obsahovat? +- mkdir +- @echo off +- [x] del přes celý disk nebo cizí složky +- pause +> Cvičení složky zakládá. Mazání do něj nepatří a neznámé mazání už vůbec ne. + +## Jaký je stav známky 5? +- prospěl +- [x] neprospěl +- průměr +- 1.A +> Prochází 1 až 4. Pětka je neprospěl. Rozhoduje vzorec KDYŽ, ne rukou psaný text. + +## Jakou roli má účet, který ve WordPressu jen píše novinky? +- Správce +- [x] Autor +- Jediný sdílený účet učitele +- Administrators ve Windows +> Autor zveřejní vlastní příspěvek. Doplňky a uživatele spravuje jeden správce. + +## Jaké jsou dvě hodnoty zásad? +- Heslo 0 znaků a zámek 8 sekund +- [x] Heslo aspoň 8 znaků a zámek po 300 sekundách +- Heslo v souboru bat a zámek vypnutý +- 300 znaků hesla a 8 sekund +> Osm znaků je délka hesla. 300 sekund je pět minut nečinnosti. diff --git a/ios/Graduately/AppModel.swift b/ios/Graduately/AppModel.swift index 5fad4f7..9d324ec 100644 --- a/ios/Graduately/AppModel.swift +++ b/ios/Graduately/AppModel.swift @@ -269,6 +269,26 @@ final class AppModel: ObservableObject { refresh() } + func markSw(_ id: Int) { + progress.markSw(id) + refresh() + } + + func markSw2(_ id: Int) { + progress.markSw2(id) + refresh() + } + + func markSw3(_ id: Int) { + progress.markSw3(id) + refresh() + } + + func markSw4(_ id: Int) { + progress.markSw4(id) + refresh() + } + func markTg4(_ id: Int) { progress.markTg4(id) refresh() diff --git a/ios/Graduately/Data/Content.swift b/ios/Graduately/Data/Content.swift index ca8da2a..dafa975 100644 --- a/ios/Graduately/Data/Content.swift +++ b/ios/Graduately/Data/Content.swift @@ -67,6 +67,10 @@ final class Content { let pg2: [J] let pg3: [J] let pg4: [J] + let sw: [J] + let sw2: [J] + let sw3: [J] + let sw4: [J] let cs: [J] let cs2: [J] let cs3: [J] @@ -145,6 +149,10 @@ final class Content { pg2 = raw.arr("pg2") pg3 = raw.arr("pg3") pg4 = raw.arr("pg4") + sw = raw.arr("sw") + sw2 = raw.arr("sw2") + sw3 = raw.arr("sw3") + sw4 = raw.arr("sw4") cs = raw.arr("cs") cs2 = raw.arr("cs2") cs3 = raw.arr("cs3") @@ -219,6 +227,10 @@ final class Content { func pg2Lesson(_ id: Int) -> J? { pg2.first { $0.int("id") == id } } func pg3Lesson(_ id: Int) -> J? { pg3.first { $0.int("id") == id } } func pg4Lesson(_ id: Int) -> J? { pg4.first { $0.int("id") == id } } + func swLesson(_ id: Int) -> J? { sw.first { $0.int("id") == id } } + func sw2Lesson(_ id: Int) -> J? { sw2.first { $0.int("id") == id } } + func sw3Lesson(_ id: Int) -> J? { sw3.first { $0.int("id") == id } } + func sw4Lesson(_ id: Int) -> J? { sw4.first { $0.int("id") == id } } func csLesson(_ id: Int) -> J? { cs.first { $0.int("id") == id } } func cs2Lesson(_ id: Int) -> J? { cs2.first { $0.int("id") == id } } func cs3Lesson(_ id: Int) -> J? { cs3.first { $0.int("id") == id } } diff --git a/ios/Graduately/Data/ProgressStore.swift b/ios/Graduately/Data/ProgressStore.swift index c9b8a64..f77c330 100644 --- a/ios/Graduately/Data/ProgressStore.swift +++ b/ios/Graduately/Data/ProgressStore.swift @@ -110,6 +110,14 @@ final class ProgressStore { func markPg3(_ id: Int) { mark("pg3.\(id)") } func pg4Done(_ id: Int) -> Bool { done("pg4.\(id)") } func markPg4(_ id: Int) { mark("pg4.\(id)") } + func swDone(_ id: Int) -> Bool { done("sw.\(id)") } + func markSw(_ id: Int) { mark("sw.\(id)") } + func sw2Done(_ id: Int) -> Bool { done("sw2.\(id)") } + func markSw2(_ id: Int) { mark("sw2.\(id)") } + func sw3Done(_ id: Int) -> Bool { done("sw3.\(id)") } + func markSw3(_ id: Int) { mark("sw3.\(id)") } + func sw4Done(_ id: Int) -> Bool { done("sw4.\(id)") } + func markSw4(_ id: Int) { mark("sw4.\(id)") } func csDone(_ id: Int) -> Bool { done("cs.\(id)") } func markCs(_ id: Int) { mark("cs.\(id)") } func cs2Done(_ id: Int) -> Bool { done("cs2.\(id)") } @@ -376,6 +384,38 @@ func summarize(_ content: Content, _ p: ProgressStore) -> ProgressSum { sum.doneUnits += 1 } } + for l in content.sw { + sum.totalEx += 1 + sum.openUnits += 1 + if p.swDone(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.sw2 { + sum.totalEx += 1 + sum.openUnits += 1 + if p.sw2Done(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.sw3 { + sum.totalEx += 1 + sum.openUnits += 1 + if p.sw3Done(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.sw4 { + sum.totalEx += 1 + sum.openUnits += 1 + if p.sw4Done(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } for l in content.cs { sum.totalEx += 1 sum.openUnits += 1 diff --git a/ios/Graduately/Route.swift b/ios/Graduately/Route.swift index 005c538..a85395c 100644 --- a/ios/Graduately/Route.swift +++ b/ios/Graduately/Route.swift @@ -137,6 +137,19 @@ enum Route: Hashable { case pg4Map case pg4Lesson(Int) case pg4Ex(Int) + case swYears + case swMap + case swLesson(Int) + case swEx(Int) + case sw2Map + case sw2Lesson(Int) + case sw2Ex(Int) + case sw3Map + case sw3Lesson(Int) + case sw3Ex(Int) + case sw4Map + case sw4Lesson(Int) + case sw4Ex(Int) case csYears case csMap case csLesson(Int) @@ -285,6 +298,19 @@ enum Route: Hashable { case .pg4Map: return "pg4map" case .pg4Lesson(let id): return "pg4unit\(id)" case .pg4Ex(let id): return "pg4ex\(id)" + case .swYears: return "swyears" + case .swMap: return "swmap" + case .swLesson(let id): return "swunit\(id)" + case .swEx(let id): return "swex\(id)" + case .sw2Map: return "sw2map" + case .sw2Lesson(let id): return "sw2unit\(id)" + case .sw2Ex(let id): return "sw2ex\(id)" + case .sw3Map: return "sw3map" + case .sw3Lesson(let id): return "sw3unit\(id)" + case .sw3Ex(let id): return "sw3ex\(id)" + case .sw4Map: return "sw4map" + case .sw4Lesson(let id): return "sw4unit\(id)" + case .sw4Ex(let id): return "sw4ex\(id)" case .csYears: return "csyears" case .csMap: return "csmap" case .csLesson(let id): return "csunit\(id)" @@ -570,6 +596,32 @@ enum Route: Hashable { return [.pgYears, .pg4Map, .pg4Lesson(id)] case .pg4Ex(let id): return [.pgYears, .pg4Map, .pg4Lesson(id), .pg4Ex(id)] + case .swYears: + return [.swYears] + case .swMap: + return [.swYears, .swMap] + case .swLesson(let id): + return [.swYears, .swMap, .swLesson(id)] + case .swEx(let id): + return [.swYears, .swMap, .swLesson(id), .swEx(id)] + case .sw2Map: + return [.swYears, .sw2Map] + case .sw2Lesson(let id): + return [.swYears, .sw2Map, .sw2Lesson(id)] + case .sw2Ex(let id): + return [.swYears, .sw2Map, .sw2Lesson(id), .sw2Ex(id)] + case .sw3Map: + return [.swYears, .sw3Map] + case .sw3Lesson(let id): + return [.swYears, .sw3Map, .sw3Lesson(id)] + case .sw3Ex(let id): + return [.swYears, .sw3Map, .sw3Lesson(id), .sw3Ex(id)] + case .sw4Map: + return [.swYears, .sw4Map] + case .sw4Lesson(let id): + return [.swYears, .sw4Map, .sw4Lesson(id)] + case .sw4Ex(let id): + return [.swYears, .sw4Map, .sw4Lesson(id), .sw4Ex(id)] case .csYears: return [.csYears] case .csMap: @@ -658,6 +710,11 @@ func routeFromPage(_ page: String) -> Route? { case "pg2map": return .pg2Map case "pg3map": return .pg3Map case "pg4map": return .pg4Map + case "swyears": return .swYears + case "swmap": return .swMap + case "sw2map": return .sw2Map + case "sw3map": return .sw3Map + case "sw4map": return .sw4Map case "csyears": return .csYears case "csmap": return .csMap case "cs2map": return .cs2Map @@ -772,6 +829,30 @@ func routeFromPage(_ page: String) -> Route? { if let m = page.wholeMatch(of: /fyzex(\d+)/), let n = Int(m.1) { return .fyzEx(n) } + if let m = page.wholeMatch(of: /sw4unit(\d+)/), let n = Int(m.1) { + return .sw4Lesson(n) + } + if let m = page.wholeMatch(of: /sw4ex(\d+)/), let n = Int(m.1) { + return .sw4Ex(n) + } + if let m = page.wholeMatch(of: /sw3unit(\d+)/), let n = Int(m.1) { + return .sw3Lesson(n) + } + if let m = page.wholeMatch(of: /sw3ex(\d+)/), let n = Int(m.1) { + return .sw3Ex(n) + } + if let m = page.wholeMatch(of: /sw2unit(\d+)/), let n = Int(m.1) { + return .sw2Lesson(n) + } + if let m = page.wholeMatch(of: /sw2ex(\d+)/), let n = Int(m.1) { + return .sw2Ex(n) + } + if let m = page.wholeMatch(of: /swunit(\d+)/), let n = Int(m.1) { + return .swLesson(n) + } + if let m = page.wholeMatch(of: /swex(\d+)/), let n = Int(m.1) { + return .swEx(n) + } if let m = page.wholeMatch(of: /pg4unit(\d+)/), let n = Int(m.1) { return .pg4Lesson(n) } diff --git a/ios/Graduately/UI/Components.swift b/ios/Graduately/UI/Components.swift index d93afdc..e0aaf2a 100644 --- a/ios/Graduately/UI/Components.swift +++ b/ios/Graduately/UI/Components.swift @@ -712,6 +712,7 @@ func subjectSymbol(_ icon: String) -> String { case "ruler": return "ruler.fill" case "code": return "chevron.left.forwardslash.chevron.right" case "brush": return "paintbrush.fill" + case "apps": return "square.grid.2x2.fill" default: return "lock.fill" } } diff --git a/ios/Graduately/UI/ExercisesMore.swift b/ios/Graduately/UI/ExercisesMore.swift index 3ae847b..783dd4d 100644 --- a/ios/Graduately/UI/ExercisesMore.swift +++ b/ios/Graduately/UI/ExercisesMore.swift @@ -1018,6 +1018,39 @@ struct PgQuizScreen: View { } } +struct SwQuizScreen: View { + @ObservedObject var vm: AppModel + let year: Int + let id: Int + + var body: some View { + let lesson: J? = { + switch year { + case 2: return vm.content.sw2Lesson(id) + case 3: return vm.content.sw3Lesson(id) + case 4: return vm.content.sw4Lesson(id) + default: return vm.content.swLesson(id) + } + }() + if let lesson { + let spec = J([ + "type": "choice", + "title": vm.tr(lesson.str("exTitleKey")), + "sub": lesson.str("quizHeadKey"), + "questions": lesson.rawArray("quiz"), + ]) + DispatchExercise(vm: vm, spec: spec, titleFallback: vm.tr(lesson.str("exTitleKey"))) { + switch year { + case 2: vm.markSw2(id) + case 3: vm.markSw3(id) + case 4: vm.markSw4(id) + default: vm.markSw(id) + } + } + } + } +} + struct FyzQuizScreen: View { @ObservedObject var vm: AppModel let year: Int diff --git a/ios/Graduately/UI/Screens.swift b/ios/Graduately/UI/Screens.swift index 54a6a28..d4535b2 100644 --- a/ios/Graduately/UI/Screens.swift +++ b/ios/Graduately/UI/Screens.swift @@ -751,6 +751,10 @@ func routeProgress(_ vm: AppModel, _ r: Route) -> (done: Int, total: Int)? { case .pg2Map: return tally(c.pg2, p.pg2Done) case .pg3Map: return tally(c.pg3, p.pg3Done) case .pg4Map: return tally(c.pg4, p.pg4Done) + case .swMap: return tally(c.sw, p.swDone) + case .sw2Map: return tally(c.sw2, p.sw2Done) + case .sw3Map: return tally(c.sw3, p.sw3Done) + case .sw4Map: return tally(c.sw4, p.sw4Done) case .csMap: return tally(c.cs, p.csDone) case .cs2Map: return tally(c.cs2, p.cs2Done) case .cs3Map: return tally(c.cs3, p.cs3Done) @@ -820,6 +824,12 @@ private func subjectSum(_ vm: AppModel, _ s: J) -> ProgressSum { + vm.content.pg2.filter { vm.progress.pg2Done($0.int("id")) }.count + vm.content.pg3.filter { vm.progress.pg3Done($0.int("id")) }.count + vm.content.pg4.filter { vm.progress.pg4Done($0.int("id")) }.count + case "swyears", "swmap", "sw2map", "sw3map", "sw4map": + sum.totalEx = vm.content.sw.count + vm.content.sw2.count + vm.content.sw3.count + vm.content.sw4.count + sum.doneEx = vm.content.sw.filter { vm.progress.swDone($0.int("id")) }.count + + vm.content.sw2.filter { vm.progress.sw2Done($0.int("id")) }.count + + vm.content.sw3.filter { vm.progress.sw3Done($0.int("id")) }.count + + vm.content.sw4.filter { vm.progress.sw4Done($0.int("id")) }.count case "tgyears", "tgmap", "tg2map", "tg3map", "tg4map": sum.totalEx = vm.content.tg.count + vm.content.tg2.count + vm.content.tg3.count + vm.content.tg4.count sum.doneEx = vm.content.tg.filter { vm.progress.tgDone($0.int("id")) }.count @@ -1129,6 +1139,27 @@ private func buildSearch(_ vm: AppModel) -> [Hit] { add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "fyz4unit\(l.int("id"))", extra: "fyzika") add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "fyz4ex\(l.int("id"))") } + add(vm.tr("Programové vybavení"), vm.tr("search_lesson"), "swyears", extra: "office windows excel mysql") + add(vm.tr("sw_year1"), vm.tr("search_lesson"), "swmap", extra: "rocnik") + add(vm.tr("sw_year2"), vm.tr("search_lesson"), "sw2map", extra: "rocnik") + add(vm.tr("sw_year3"), vm.tr("search_lesson"), "sw3map", extra: "rocnik") + add(vm.tr("sw_year4"), vm.tr("search_lesson"), "sw4map", extra: "rocnik") + for l in vm.content.sw { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "swunit\(l.int("id"))", extra: "windows") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "swex\(l.int("id"))") + } + for l in vm.content.sw2 { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "sw2unit\(l.int("id"))", extra: "word excel") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "sw2ex\(l.int("id"))") + } + for l in vm.content.sw3 { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "sw3unit\(l.int("id"))", extra: "access mysql") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "sw3ex\(l.int("id"))") + } + for l in vm.content.sw4 { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "sw4unit\(l.int("id"))", extra: "wordpress") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "sw4ex\(l.int("id"))") + } add(vm.tr("Prezentační grafika"), vm.tr("search_lesson"), "pgyears", extra: "photoshop premiere illustrator") add(vm.tr("pg_year1"), vm.tr("search_lesson"), "pgmap", extra: "rocnik") add(vm.tr("pg_year2"), vm.tr("search_lesson"), "pg2map", extra: "rocnik") @@ -1976,6 +2007,47 @@ struct CsYearsScreen: View { } } +struct SwYearsScreen: View { + @ObservedObject var vm: AppModel + + var body: some View { + #if os(macOS) + let years: [(String, String, Route)] = [ + ("sw_year1", "sw_sub", .swMap), + ("sw_year2", "sw2_sub", .sw2Map), + ("sw_year3", "sw3_sub", .sw3Map), + ("sw_year4", "sw4_sub", .sw4Map), + ] + MacFillGrid(count: years.count, minWidth: 260, minHeight: 200) { i in + NavigationLink(value: years[i].2) { + MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).", progress: routeProgress(vm, years[i].2)) + } + .buttonStyle(PressScaleStyle()) + } + .navigationTitle(vm.tr("Programové vybavení")) + .navigationSubtitle(vm.tr("sw_years_sub")) + #else + List { + NavigationLink(value: Route.swMap) { + ListRowLabel(title: vm.tr("sw_year1"), subtitle: vm.tr("sw_sub"), progress: routeProgress(vm, Route.swMap)) + } + NavigationLink(value: Route.sw2Map) { + ListRowLabel(title: vm.tr("sw_year2"), subtitle: vm.tr("sw2_sub"), progress: routeProgress(vm, Route.sw2Map)) + } + NavigationLink(value: Route.sw3Map) { + ListRowLabel(title: vm.tr("sw_year3"), subtitle: vm.tr("sw3_sub"), progress: routeProgress(vm, Route.sw3Map)) + } + NavigationLink(value: Route.sw4Map) { + ListRowLabel(title: vm.tr("sw_year4"), subtitle: vm.tr("sw4_sub"), progress: routeProgress(vm, Route.sw4Map)) + } + } + .navigationTitle(vm.tr("Programové vybavení")) + .navigationSubtitle(vm.tr("sw_years_sub")) + .appListStyle() + #endif + } +} + struct PgYearsScreen: View { @ObservedObject var vm: AppModel @@ -3175,6 +3247,72 @@ struct RouteDestination: View { if let l = vm.content.cs4Lesson(id) { CodeLessonScreen(vm: vm, lesson: l, onSuccess: { vm.markCs4(id) }) } + case .swYears: + SwYearsScreen(vm: vm) + case .swMap: + LessonListScreen( + vm: vm, + title: vm.tr("sw_year1"), + subtitle: vm.tr("sw_sub"), + rows: vm.content.sw.map { l in + let id = l.int("id") + return LessonRow(id: "s\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.swDone(id), destination: .swLesson(id)) + } + ) + case .swLesson(let id): + if let l = vm.content.swLesson(id) { + SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .swEx(id)) + } + case .swEx(let id): + SwQuizScreen(vm: vm, year: 1, id: id) + case .sw2Map: + LessonListScreen( + vm: vm, + title: vm.tr("sw_year2"), + subtitle: vm.tr("sw2_sub"), + rows: vm.content.sw2.map { l in + let id = l.int("id") + return LessonRow(id: "s2\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.sw2Done(id), destination: .sw2Lesson(id)) + } + ) + case .sw2Lesson(let id): + if let l = vm.content.sw2Lesson(id) { + SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .sw2Ex(id)) + } + case .sw2Ex(let id): + SwQuizScreen(vm: vm, year: 2, id: id) + case .sw3Map: + LessonListScreen( + vm: vm, + title: vm.tr("sw_year3"), + subtitle: vm.tr("sw3_sub"), + rows: vm.content.sw3.map { l in + let id = l.int("id") + return LessonRow(id: "s3\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.sw3Done(id), destination: .sw3Lesson(id)) + } + ) + case .sw3Lesson(let id): + if let l = vm.content.sw3Lesson(id) { + SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .sw3Ex(id)) + } + case .sw3Ex(let id): + SwQuizScreen(vm: vm, year: 3, id: id) + case .sw4Map: + LessonListScreen( + vm: vm, + title: vm.tr("sw_year4"), + subtitle: vm.tr("sw4_sub"), + rows: vm.content.sw4.map { l in + let id = l.int("id") + return LessonRow(id: "s4\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.sw4Done(id), destination: .sw4Lesson(id)) + } + ) + case .sw4Lesson(let id): + if let l = vm.content.sw4Lesson(id) { + SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .sw4Ex(id)) + } + case .sw4Ex(let id): + SwQuizScreen(vm: vm, year: 4, id: id) case .pgYears: PgYearsScreen(vm: vm) case .pgMap: diff --git a/scripts/build-content.py b/scripts/build-content.py index 12bf473..ca44c03 100644 --- a/scripts/build-content.py +++ b/scripts/build-content.py @@ -1110,6 +1110,8 @@ STD = [ ("technicka-grafika/3-rocnik", "tg3"), ("technicka-grafika/4-rocnik", "tg4"), ("prezentacni-grafika/1-rocnik", "pg"), ("prezentacni-grafika/2-rocnik", "pg2"), ("prezentacni-grafika/3-rocnik", "pg3"), ("prezentacni-grafika/4-rocnik", "pg4"), + ("programove-vybaveni/1-rocnik", "sw"), ("programove-vybaveni/2-rocnik", "sw2"), + ("programove-vybaveni/3-rocnik", "sw3"), ("programove-vybaveni/4-rocnik", "sw4"), ] CODE = [ ("programovani/1-rocnik", "cs"), ("programovani/2-rocnik", "cs2"), From 235436dab001272b67f56608babac73da8c0dcdc Mon Sep 17 00:00:00 2001 From: pepaskopec-blip Date: Sun, 11 Oct 2026 19:35:03 +0200 Subject: [PATCH 7/7] Follow the Mac system language in the menu bar. Co-authored-by: Cursor --- macos/Graduately.xcodeproj/project.pbxproj | 19 ++++++++++++++++++- macos/Graduately/cs.lproj/Localizable.strings | 1 + macos/Graduately/en.lproj/Localizable.strings | 1 + 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 macos/Graduately/cs.lproj/Localizable.strings create mode 100644 macos/Graduately/en.lproj/Localizable.strings diff --git a/macos/Graduately.xcodeproj/project.pbxproj b/macos/Graduately.xcodeproj/project.pbxproj index 6baafad..42b58cc 100644 --- a/macos/Graduately.xcodeproj/project.pbxproj +++ b/macos/Graduately.xcodeproj/project.pbxproj @@ -23,6 +23,7 @@ B101000000000000000014 /* Exercises.swift in Sources */ = {isa = PBXBuildFile; fileRef = B201000000000000000014 /* Exercises.swift */; }; B101000000000000000015 /* ExercisesMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B201000000000000000015 /* ExercisesMore.swift */; }; B101000000000000000016 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B201000000000000000016 /* Assets.xcassets */; }; + B101000000000000000018 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = B201000000000000000020 /* Localizable.strings */; }; B101000000000000000017 /* PathMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = B201000000000000000019 /* PathMap.swift */; }; /* End PBXBuildFile section */ @@ -46,6 +47,8 @@ B201000000000000000017 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B201000000000000000018 /* Graduately.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Graduately.entitlements; sourceTree = ""; }; B201000000000000000019 /* PathMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PathMap.swift; path = ../ios/Graduately/UI/PathMap.swift; sourceTree = SOURCE_ROOT; }; + B201000000000000000021 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + B201000000000000000022 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = ""; }; B301000000000000000001 /* Graduately.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Graduately.app; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -79,6 +82,7 @@ B501000000000000000004 /* Update */, B501000000000000000005 /* UI */, B201000000000000000016 /* Assets.xcassets */, + B201000000000000000020 /* Localizable.strings */, B201000000000000000017 /* Info.plist */, B201000000000000000018 /* Graduately.entitlements */, ); @@ -128,6 +132,18 @@ }; /* End PBXGroup section */ +/* Begin PBXVariantGroup section */ + B201000000000000000020 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + B201000000000000000021 /* en */, + B201000000000000000022 /* cs */, + ); + name = Localizable.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + /* Begin PBXNativeTarget section */ B601000000000000000001 /* Graduately */ = { isa = PBXNativeTarget; @@ -164,7 +180,7 @@ }; buildConfigurationList = B801000000000000000001 /* Build configuration list for PBXProject "Graduately" */; compatibilityVersion = "Xcode 14.0"; - developmentRegion = cs; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -187,6 +203,7 @@ buildActionMask = 2147483647; files = ( B101000000000000000016 /* Assets.xcassets in Resources */, + B101000000000000000018 /* Localizable.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/macos/Graduately/cs.lproj/Localizable.strings b/macos/Graduately/cs.lproj/Localizable.strings new file mode 100644 index 0000000..a080c4c --- /dev/null +++ b/macos/Graduately/cs.lproj/Localizable.strings @@ -0,0 +1 @@ +/* Označí češtinu jako podporovaný jazyk, aby nabídky macOS zůstaly česky na českém systému. */ diff --git a/macos/Graduately/en.lproj/Localizable.strings b/macos/Graduately/en.lproj/Localizable.strings new file mode 100644 index 0000000..3c0e8ca --- /dev/null +++ b/macos/Graduately/en.lproj/Localizable.strings @@ -0,0 +1 @@ +/* Marks English as a supported language so macOS menus follow an English system. */