Compare commits

..

No commits in common. "235436dab001272b67f56608babac73da8c0dcdc" and "51101a93e2a9197f6a5f787ef475a32cc6f914a2" have entirely different histories.

293 changed files with 455 additions and 13641 deletions

View file

@ -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; 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. ostatní jsou zatím zamčené. Aplikace se po instalaci aktualizuje sama.
| Úvod | Němčina | Neue Freunde | | Úvod | Předměty | Německá cesta |
| ---- | -------- | ------------ | | ---- | -------- | ------------- |
| ![Úvod](assets/home.png) | ![Němčina](assets/deutsch.png) | ![Neue Freunde](assets/neue-freunde.png) | | ![Úvod](assets/welcomescreen.png) | ![Předměty](assets/subjectsscreen.png) | ![Německá cesta](assets/germanlection.png) |
### Instalace ### Instalace
@ -71,15 +71,6 @@ 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í - **Technické vybavení** — 4 ročníky: 1. ročník má 29 lekcí
(architektura, data, skříň, deska, karty, disky a vstup), (architektura, data, skříň, deska, karty, disky a vstup),
ročníky 2–4 po 8 lekcích (monitory, sestavení, RAID, servery, údržba) 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é 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 - **Český jazyk a literatura** — literatura (4 ročníky, 32 lekcí podle osnov
středních škol), mluvnice (20 cvičení) a maturitní četba středních škol), mluvnice (20 cvičení) a maturitní četba
(82 knih: zápisky, kvíz, sestavení děje) (82 knih: zápisky, kvíz, sestavení děje)
@ -99,10 +90,10 @@ Intel Mac, jiná architektura nebo úpravy kódu → [sestavení ze zdroje](#ses
### Použití ### Použití
1. **Úvod** ukáže postup a mřížku předmětů. Každý předmět spustí svou cestu. 1. **Pokračuj** otevře mapu předmětů.
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. 2. Otevřený předmět (vlajka, Wi‑Fi, čip, česká vlajka, váhy, funkce, atom, baňka) vede na učební cestu.
3. Z cesty se otevře výklad, poslech, čtení, psaní nebo kvíz. 3. Uzly cvičení / lekcí otevřou obsah; zamčené nic nedělají.
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`. 4. **Hledat** (lupa), **Statistiky** (graf) a **Nastavení** (ozubené kolo) jsou vpravo nahoře.
### Sestavení ze zdroje ### Sestavení ze zdroje
@ -207,9 +198,9 @@ Licence: [GPL-3.0](LICENSE).
The UI is Czech and English. Open subjects are listed below; the rest are The UI is Czech and English. Open subjects are listed below; the rest are
still locked. After install the app updates itself. still locked. After install the app updates itself.
| Welcome | German | Neue Freunde | | Welcome | Subjects | German path |
| ------- | ------ | ----------- | | ------- | -------- | ----------- |
| ![Welcome](assets/home.png) | ![German](assets/deutsch.png) | ![Neue Freunde](assets/neue-freunde.png) | | ![Welcome](assets/welcomescreen.png) | ![Subjects](assets/subjectsscreen.png) | ![German path](assets/germanlection.png) |
### Install ### Install
@ -268,10 +259,6 @@ Intel Mac, another architecture, or hacking on the code →
- **Computer hardware** — 4 years: year 1 has 29 lessons - **Computer hardware** — 4 years: year 1 has 29 lessons
(architecture, data, case, board, cards, drives and input), (architecture, data, case, board, cards, drives and input),
years 2–4 have 8 lessons each (displays, assembly, RAID, servers, maintenance) 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 - **Czech language** — literature (4 years, 32 lessons from the secondary-school
syllabus), grammar (20 exercises) and required reading syllabus), grammar (20 exercises) and required reading
(82 books: notes, quiz, plot ordering) (82 books: notes, quiz, plot ordering)
@ -292,10 +279,10 @@ Intel Mac, another architecture, or hacking on the code →
### Usage ### Usage
1. **Welcome** shows progress and the subject grid. An open subject starts its path. Locked tiles do nothing yet: presentation graphics and software. 1. **Continue** opens the subject map.
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. 2. An open subject (flag, Wi‑Fi, chip, Czech flag, scales, function, atom, flask) opens its path.
3. A path opens an overview, listening, reading, writing or a quiz. 3. Exercise / lesson nodes open content; locked nodes do nothing.
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`. 4. **Search** (magnifier), **Statistics** (chart) and **Settings** (gear) sit in the top-right.
### Build from source ### Build from source

View file

@ -1,128 +0,0 @@
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<String>, 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<String, String> {
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) = """
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>$framework</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
<LangVersion>latest</LangVersion>
</PropertyGroup>
</Project>
""".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
}
}

View file

@ -1,10 +1,8 @@
package org.maturita.maturita.desktop package org.maturita.maturita.desktop
import org.maturita.maturita.platform.CSharpRun
import org.maturita.maturita.platform.Platform import org.maturita.maturita.platform.Platform
import org.maturita.maturita.platform.Prefs import org.maturita.maturita.platform.Prefs
import org.maturita.maturita.platform.Speaker import org.maturita.maturita.platform.Speaker
import javax.swing.SwingUtilities
class DesktopPlatform( class DesktopPlatform(
override val commit: String, override val commit: String,
@ -16,11 +14,4 @@ class DesktopPlatform(
override val updater = DesktopUpdater(commit, install) override val updater = DesktopUpdater(commit, install)
override fun speaker(): Speaker? = DesktopSpeaker.create() 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()
}
} }

View file

@ -40,8 +40,6 @@ fun main() {
val info = Properties().apply { resourceText("build-info.properties")?.let { load(it.reader()) } } val info = Properties().apply { resourceText("build-info.properties")?.let { load(it.reader()) } }
val platform = DesktopPlatform(info.getProperty("commit") ?: "dev", prefs, install) val platform = DesktopPlatform(info.getProperty("commit") ?: "dev", prefs, install)
val vm = AppViewModel(Content.parse(resourceText("content.json")), platform) 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") val icon = Thread.currentThread().contextClassLoader.getResourceAsStream("icon.png")
?.use { ImageIO.read(it) }?.let { BitmapPainter(it.toComposeImageBitmap()) } ?.use { ImageIO.read(it) }?.let { BitmapPainter(it.toComposeImageBitmap()) }

View file

@ -23,8 +23,6 @@ import org.maturita.maturita.data.ColorMode
import org.maturita.maturita.data.Palette import org.maturita.maturita.data.Palette
import org.maturita.maturita.ui.BookListScreen import org.maturita.maturita.ui.BookListScreen
import org.maturita.maturita.ui.BookScreen 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.CzechMapScreen
import org.maturita.maturita.ui.GermanExercise import org.maturita.maturita.ui.GermanExercise
import org.maturita.maturita.ui.HomeScreen import org.maturita.maturita.ui.HomeScreen
@ -32,12 +30,6 @@ import org.maturita.maturita.ui.HwQuizScreen
import org.maturita.maturita.ui.HwYearsScreen import org.maturita.maturita.ui.HwYearsScreen
import org.maturita.maturita.ui.LessonListScreen import org.maturita.maturita.ui.LessonListScreen
import org.maturita.maturita.ui.FyzQuizScreen 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 import org.maturita.maturita.ui.MathPracticeScreen
import org.maturita.maturita.ui.MatYearsScreen import org.maturita.maturita.ui.MatYearsScreen
import org.maturita.maturita.ui.FyzYearsScreen import org.maturita.maturita.ui.FyzYearsScreen
@ -533,222 +525,6 @@ private fun MaturitaAppBody(vm: AppViewModel) {
} }
} }
is Route.Fyz4Ex -> FyzQuizScreen(vm, 4, r.id) 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.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.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"),
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.SciMap -> SciMapScreen(vm)
Route.ChemMap -> LessonListScreen( Route.ChemMap -> LessonListScreen(
vm, vm.tr("Chemie"), vm.tr("chem_sub"), vm, vm.tr("Chemie"), vm.tr("chem_sub"),

View file

@ -242,86 +242,6 @@ class AppViewModel(val content: Content, val platform: Platform) {
refresh() 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 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 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()
}
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) { fun markMluv(n: Int) {
progress.markMluv(n) progress.markMluv(n)
refresh() refresh()

View file

@ -105,54 +105,6 @@ sealed class Route(val page: String) {
data object Fyz4Map : Route("fyz4map") data object Fyz4Map : Route("fyz4map")
data class Fyz4Lesson(val id: Int) : Route("fyz4unit$id") data class Fyz4Lesson(val id: Int) : Route("fyz4unit$id")
data class Fyz4Ex(val id: Int) : Route("fyz4ex$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 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 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")
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 CzechMap : Route("czechmap")
data object Mluvnice : Route("mluvnice") data object Mluvnice : Route("mluvnice")
data class MluvEx(val n: Int) : Route("mluve$n") data class MluvEx(val n: Int) : Route("mluve$n")
@ -285,54 +237,6 @@ sealed class Route(val page: String) {
Fyz4Map -> listOf(Subjects, FyzYears, Fyz4Map) Fyz4Map -> listOf(Subjects, FyzYears, Fyz4Map)
is Fyz4Lesson -> listOf(Subjects, FyzYears, Fyz4Map, this) is Fyz4Lesson -> listOf(Subjects, FyzYears, Fyz4Map, this)
is Fyz4Ex -> listOf(Subjects, FyzYears, Fyz4Map, Fyz4Lesson(id), 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)
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)
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)
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) CzechMap -> listOf(Subjects, CzechMap)
Mluvnice -> listOf(Subjects, CzechMap, Mluvnice) Mluvnice -> listOf(Subjects, CzechMap, Mluvnice)
is MluvEx -> listOf(Subjects, CzechMap, Mluvnice, this) is MluvEx -> listOf(Subjects, CzechMap, Mluvnice, this)
@ -385,26 +289,6 @@ fun routeFromPage(page: String): Route? = when (page) {
"fyz2map" -> Route.Fyz2Map "fyz2map" -> Route.Fyz2Map
"fyz3map" -> Route.Fyz3Map "fyz3map" -> Route.Fyz3Map
"fyz4map" -> Route.Fyz4Map "fyz4map" -> Route.Fyz4Map
"tgyears" -> Route.TgYears
"tgmap" -> Route.TgMap
"tg2map" -> Route.Tg2Map
"tg3map" -> Route.Tg3Map
"tg4map" -> Route.Tg4Map
"pgyears" -> Route.PgYears
"pgmap" -> Route.PgMap
"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
"cs3map" -> Route.Cs3Map
"cs4map" -> Route.Cs4Map
"czechmap" -> Route.CzechMap "czechmap" -> Route.CzechMap
"mluvnice" -> Route.Mluvnice "mluvnice" -> Route.Mluvnice
"readinglist" -> Route.ReadingList "readinglist" -> Route.ReadingList
@ -502,90 +386,6 @@ fun routeFromPage(page: String): Route? = when (page) {
Regex("""fyzex(\d+)""").matchEntire(page)?.let { Regex("""fyzex(\d+)""").matchEntire(page)?.let {
return Route.FyzEx(it.groupValues[1].toInt()) 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("""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("""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())
}
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 { Regex("""lit4unit(\d+)""").matchEntire(page)?.let {
return Route.Lit4Lesson(it.groupValues[1].toInt()) return Route.Lit4Lesson(it.groupValues[1].toInt())
} }

View file

@ -56,22 +56,6 @@ class Content(root: JSONObject) {
val fyz2 = raw.arr("fyz2") val fyz2 = raw.arr("fyz2")
val fyz3 = raw.arr("fyz3") val fyz3 = raw.arr("fyz3")
val fyz4 = raw.arr("fyz4") 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 pg = raw.arr("pg")
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")
val cs4 = raw.arr("cs4")
val mat0 = raw.arr("mat0") val mat0 = raw.arr("mat0")
val mat = raw.arr("mat") val mat = raw.arr("mat")
val mat2 = raw.arr("mat2") val mat2 = raw.arr("mat2")
@ -124,22 +108,6 @@ class Content(root: JSONObject) {
fun fyz2Lesson(id: Int) = fyz2.firstOrNull { it.int("id") == id } fun fyz2Lesson(id: Int) = fyz2.firstOrNull { it.int("id") == id }
fun fyz3Lesson(id: Int) = fyz3.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 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 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 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 }
fun cs4Lesson(id: Int) = cs4.firstOrNull { it.int("id") == id }
fun mat0Lesson(id: Int) = mat0.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 matLesson(id: Int) = mat.firstOrNull { it.int("id") == id }
fun mat2Lesson(id: Int) = mat2.firstOrNull { it.int("id") == id } fun mat2Lesson(id: Int) = mat2.firstOrNull { it.int("id") == id }

View file

@ -91,38 +91,6 @@ class ProgressStore(private val prefs: Prefs, content: Content) {
fun markFyz3(id: Int) { mark("fyz3.$id") } fun markFyz3(id: Int) { mark("fyz3.$id") }
fun fyz4Done(id: Int) = done("fyz4.$id") fun fyz4Done(id: Int) = done("fyz4.$id")
fun markFyz4(id: Int) { mark("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 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 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")
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 mat0Done(id: Int) = done("mat0.$id")
fun markMat0(id: Int) { mark("mat0.$id") } fun markMat0(id: Int) { mark("mat0.$id") }
fun matDone(id: Int) = done("mat.$id") fun matDone(id: Int) = done("mat.$id")
@ -312,134 +280,6 @@ fun summarize(content: Content, p: ProgressStore): ProgressSum {
sum.doneUnits += 1 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.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.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
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 -> content.mat0.forEach { l ->
sum.totalEx += 1 sum.totalEx += 1
sum.openUnits += 1 sum.openUnits += 1

View file

@ -18,17 +18,7 @@ interface Platform {
/** A speech engine for listening exercises, or null when there is none. */ /** A speech engine for listening exercises, or null when there is none. */
fun speaker(): Speaker? 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. */ /** The subset of Android's SharedPreferences the app uses. */
interface Prefs { interface Prefs {

View file

@ -1,303 +0,0 @@
package org.maturita.maturita.ui
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.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
/** 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(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
status = "running"
output = vm.tr("cs_running")
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()
else -> result.output.ifEmpty { vm.tr("cs_empty") }
}
if (result.status == "ok") onSuccess()
}
}
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()).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))
}
}
}
}
}
@Composable
private fun Explanation(lesson: J) {
Column(verticalArrangement = Arrangement.spacedBy(28.dp)) {
lesson.arr("slides").forEach { SlideContent(it) }
}
}
@Composable
private fun EditorCard(
vm: AppViewModel,
code: String,
onCode: (String) -> Unit,
running: Boolean,
onRun: () -> Unit,
onReset: () -> Unit,
compact: Boolean,
modifier: Modifier,
) {
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)
OutlinedTextField(
value = stdin,
onValueChange = onStdin,
textStyle = codeStyle(),
minLines = 1,
maxLines = 4,
modifier = Modifier.fillMaxWidth(),
)
}
}
@Composable
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),
)
}
}
}
@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]*?\\*/"),
)
}
}

View file

@ -8,22 +8,15 @@ import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically import androidx.compose.animation.shrinkVertically
import androidx.compose.animation.animateColorAsState import androidx.compose.animation.animateColorAsState
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement 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.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets 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.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.heightIn
@ -31,17 +24,7 @@ import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.windowInsetsPadding 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.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
@ -61,6 +44,7 @@ import androidx.compose.material3.FilterChip
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.LargeTopAppBar import androidx.compose.material3.LargeTopAppBar
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.ListItem import androidx.compose.material3.ListItem
import androidx.compose.material3.ListItemDefaults import androidx.compose.material3.ListItemDefaults
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@ -166,19 +150,7 @@ fun AppNavBar(vm: AppViewModel) {
private fun navIconColor(selected: Boolean): Color = private fun navIconColor(selected: Boolean): Color =
if (selected) MaterialTheme.colorScheme.onSecondaryContainer else MaterialTheme.colorScheme.onSurfaceVariant if (selected) MaterialTheme.colorScheme.onSecondaryContainer else MaterialTheme.colorScheme.onSurfaceVariant
/** Lists and exercises stop growing here so they keep their shape in wide desktop windows. */ /** Tab root: large collapsing title, search action, bottom navigation. */
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) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun RootScaffold( fun RootScaffold(
@ -187,40 +159,24 @@ fun RootScaffold(
showSearch: Boolean = true, showSearch: Boolean = true,
content: LazyListScope.() -> Unit, content: LazyListScope.() -> Unit,
) { ) {
BoxWithConstraints { val scroll = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState())
val wide = maxWidth >= 600.dp Scaffold(
val scroll = if (wide) { modifier = Modifier.nestedScroll(scroll.nestedScrollConnection),
TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) topBar = {
} else { LargeTopAppBar(
TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()) title = { Text(title, maxLines = 1, overflow = TextOverflow.Ellipsis) },
} actions = {
val actions: @Composable RowScope.() -> Unit = {
if (showSearch) { if (showSearch) {
IconButton(onClick = { vm.searchOpen = true }) { IconButton(onClick = { vm.searchOpen = true }) {
Icon(Icons.Filled.Search, contentDescription = vm.tr("search_placeholder")) 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, scrollBehavior = scroll,
) )
} else {
LargeTopAppBar(
title = { Text(title, maxLines = 1, overflow = TextOverflow.Ellipsis) },
actions = actions,
scrollBehavior = scroll,
)
}
}, },
bottomBar = { AppNavBar(vm) }, bottomBar = { AppNavBar(vm) },
) { inner -> ) { inner ->
CenteredColumn {
LazyColumn( LazyColumn(
contentPadding = PaddingValues( contentPadding = PaddingValues(
top = inner.calculateTopPadding(), top = inner.calculateTopPadding(),
@ -229,8 +185,6 @@ fun RootScaffold(
) { content() } ) { content() }
} }
} }
}
}
/** Pushed screen: back arrow, title with optional subtitle, optional bottom action bar. */ /** Pushed screen: back arrow, title with optional subtitle, optional bottom action bar. */
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@ -240,7 +194,6 @@ fun DetailScaffold(
title: String, title: String,
subtitle: String? = null, subtitle: String? = null,
bottomBar: (@Composable () -> Unit)? = null, bottomBar: (@Composable () -> Unit)? = null,
maxContentWidth: Dp = ContentMaxWidth,
content: @Composable (PaddingValues) -> Unit, content: @Composable (PaddingValues) -> Unit,
) { ) {
val scroll = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) val scroll = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
@ -271,9 +224,8 @@ fun DetailScaffold(
) )
}, },
bottomBar = { bottomBar?.invoke() }, bottomBar = { bottomBar?.invoke() },
) { inner -> content = content,
CenteredColumn(maxContentWidth) { content(inner) } )
}
} }
/** Feedback line plus the primary action, anchored at the bottom of an exercise. */ /** Feedback line plus the primary action, anchored at the bottom of an exercise. */
@ -290,8 +242,6 @@ fun ActionBar(
Column( Column(
Modifier Modifier
.fillMaxWidth() .fillMaxWidth()
.wrapContentWidth(Alignment.CenterHorizontally)
.widthIn(max = ActionMaxWidth)
.windowInsetsPadding(WindowInsets.navigationBars) .windowInsetsPadding(WindowInsets.navigationBars)
.padding(horizontal = 16.dp, vertical = 12.dp), .padding(horizontal = 16.dp, vertical = 12.dp),
) { ) {
@ -326,27 +276,6 @@ 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. */ /** One navigable row: done/pending/locked state on the left, chevron on the right. */
@Composable @Composable
fun NavRow( fun NavRow(
@ -355,33 +284,15 @@ fun NavRow(
done: Boolean = false, done: Boolean = false,
locked: Boolean = false, locked: Boolean = false,
leading: (@Composable () -> Unit)? = null, leading: (@Composable () -> Unit)? = null,
progress: Pair<Int, Int>? = null,
onClick: (() -> Unit)?, onClick: (() -> Unit)?,
) { ) {
val enabled = !locked && onClick != null val enabled = !locked && onClick != null
val textColor = if (enabled) MaterialTheme.colorScheme.onSurface else MaterialTheme.colorScheme.onSurfaceVariant val textColor = if (enabled) MaterialTheme.colorScheme.onSurface else MaterialTheme.colorScheme.onSurfaceVariant
val rowModifier = if (enabled) Modifier.clickable(onClick = onClick!!) else Modifier val rowModifier = if (enabled) Modifier.clickable(onClick = onClick!!) else Modifier
val shownSubtitle = subtitle?.takeIf { it.isNotBlank() }
val shownProgress = progress?.takeIf { it.second > 0 }
ListItem( ListItem(
headlineContent = { Text(title, color = textColor) }, headlineContent = { Text(title, color = textColor) },
supportingContent = if (shownSubtitle == null && shownProgress == null) null else { supportingContent = subtitle?.takeIf { it.isNotBlank() }?.let {
{ { Text(it, color = MaterialTheme.colorScheme.onSurfaceVariant) }
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 = { leadingContent = {
when { when {
@ -417,38 +328,19 @@ fun SmoothProgress(
progress: Float, progress: Float,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
color: Color = MaterialTheme.colorScheme.primary, color: Color = MaterialTheme.colorScheme.primary,
trackColor: Color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.1f), trackColor: Color = MaterialTheme.colorScheme.surfaceVariant,
) { ) {
val animated by animateFloatAsState( val animated by animateFloatAsState(
targetValue = progress.coerceIn(0f, 1f), targetValue = progress.coerceIn(0f, 1f),
animationSpec = tween(durationMillis = 380), animationSpec = tween(durationMillis = 380),
label = "progress", label = "progress",
) )
Box(modifier.heightIn(min = 4.dp).clip(CircleShape).background(trackColor)) { LinearProgressIndicator(
if (animated > 0f) { progress = { animated },
Box(Modifier.fillMaxHeight().fillMaxWidth(animated).clip(CircleShape).background(color)) modifier = modifier,
} color = color,
} trackColor = trackColor,
} )
/** 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 @Composable

View file

@ -1082,78 +1082,6 @@ fun ChoiceLessonScreen(vm: AppViewModel, lesson: J?, onDone: () -> Unit) {
DispatchExercise(vm, spec, vm.tr(lesson.str("exTitleKey")), onDone) 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 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 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 @Composable
fun FyzQuizScreen(vm: AppViewModel, year: Int, id: Int) { fun FyzQuizScreen(vm: AppViewModel, year: Int, id: Int) {
val lesson = when (year) { val lesson = when (year) {

View file

@ -25,13 +25,7 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.MenuBook 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
import androidx.compose.material.icons.filled.Bolt 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.Functions
import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Close
@ -41,7 +35,6 @@ import androidx.compose.material.icons.filled.Groups
import androidx.compose.material.icons.filled.Lock import androidx.compose.material.icons.filled.Lock
import androidx.compose.material.icons.filled.Quiz import androidx.compose.material.icons.filled.Quiz
import androidx.compose.material.icons.filled.Science 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.filled.Search
import androidx.compose.material.icons.outlined.Lightbulb import androidx.compose.material.icons.outlined.Lightbulb
import androidx.compose.material3.Card import androidx.compose.material3.Card
@ -106,61 +99,50 @@ fun HomeScreen(vm: AppViewModel) {
item { item {
Card( Card(
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 8.dp), modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 8.dp),
colors = CardDefaults.cardColors( colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.primaryContainer),
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
contentColor = MaterialTheme.colorScheme.onSurface,
),
) { ) {
Row(Modifier.padding(20.dp), verticalAlignment = Alignment.CenterVertically) { Column(Modifier.padding(20.dp)) {
Column(Modifier.weight(1f)) { Text(
Text(vm.tr("welcome_title"), style = MaterialTheme.typography.headlineMedium) vm.tr("welcome_title"),
style = MaterialTheme.typography.headlineMedium,
color = MaterialTheme.colorScheme.onPrimaryContainer,
)
Spacer(Modifier.height(8.dp)) Spacer(Modifier.height(8.dp))
Text( Text(
vm.tr(vm.platform.welcomeKey), vm.tr(vm.platform.welcomeKey),
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.8f),
) )
} Spacer(Modifier.height(16.dp))
Spacer(Modifier.width(16.dp)) SmoothProgress(
ProgressRing(pct, Modifier.size(104.dp)) { progress = pct,
Column(horizontalAlignment = Alignment.CenterHorizontally) { modifier = Modifier.fillMaxWidth().height(6.dp).clip(CircleShape),
Text(vm.fmt("stats_pct_fmt", (pct * 100).toInt()), style = MaterialTheme.typography.titleLarge) color = MaterialTheme.colorScheme.onPrimaryContainer,
trackColor = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.18f),
)
Spacer(Modifier.height(6.dp))
Text( Text(
vm.fmt("stats_ex_fmt", sum.doneEx, sum.totalEx), vm.fmt("stats_ex_fmt", sum.doneEx, sum.totalEx),
style = MaterialTheme.typography.labelSmall, style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.8f),
) )
} }
} }
} }
}
}
if (vm.update.canInstall || vm.update.status == "downloading" || vm.update.status == "staged") { if (vm.update.canInstall || vm.update.status == "downloading" || vm.update.status == "staged") {
item { UpdateBanner(vm) } 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")) } item { SectionHeader(vm.tr("subjects_title")) }
items(open) { s -> items(vm.content.subjects) { s ->
val part = subjectSum(vm, s) val open = s.bool("open")
NavRow( NavRow(
title = vm.tr(s.str("key")), title = vm.tr(s.str("key")),
leading = { SubjectIcon(s.str("icon"), true) }, subtitle = if (open) null else vm.tr("stats_locked"),
progress = part.doneEx to part.totalEx, locked = !open,
onClick = { vm.goPage(s.str("target")) }, leading = { SubjectIcon(s.str("icon"), open) },
onClick = if (open) ({ vm.goPage(s.str("target")) }) else null,
) )
} }
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,
)
}
}
} }
} }
@ -170,18 +152,14 @@ private fun ChangelogCard(vm: AppViewModel) {
val blocks = vm.content.changelog.take(3) val blocks = vm.content.changelog.take(3)
Card( Card(
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 8.dp), modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 8.dp),
colors = CardDefaults.cardColors( colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.tertiaryContainer),
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
contentColor = MaterialTheme.colorScheme.onSurface,
),
) { ) {
Column(Modifier.padding(20.dp)) { Column(Modifier.padding(20.dp)) {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
Icon(Icons.Filled.AutoAwesome, null, tint = MaterialTheme.colorScheme.primary, modifier = Modifier.size(20.dp))
Spacer(Modifier.width(8.dp))
Text( Text(
vm.tr("changelog_title"), vm.tr("changelog_title"),
style = MaterialTheme.typography.titleMedium, style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onTertiaryContainer,
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
) )
val date = blocks.firstOrNull()?.str("date").orEmpty() val date = blocks.firstOrNull()?.str("date").orEmpty()
@ -189,7 +167,7 @@ private fun ChangelogCard(vm: AppViewModel) {
Text( Text(
date, date,
style = MaterialTheme.typography.labelMedium, style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onTertiaryContainer.copy(alpha = 0.7f),
) )
} }
} }
@ -200,6 +178,7 @@ private fun ChangelogCard(vm: AppViewModel) {
Text( Text(
"• $line", "• $line",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onTertiaryContainer,
modifier = Modifier.padding(bottom = 4.dp), modifier = Modifier.padding(bottom = 4.dp),
) )
} }
@ -246,10 +225,6 @@ private fun SubjectIcon(icon: String, open: Boolean) {
"flask" -> Icon(Icons.Filled.Science, null, tint = tint, modifier = Modifier.size(26.dp)) "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)) "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)) "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))
"apps" -> Icon(Icons.Filled.Apps, null, tint = tint, modifier = Modifier.size(26.dp))
else -> Icon(Icons.Filled.Lock, null, tint = tint) else -> Icon(Icons.Filled.Lock, null, tint = tint)
} }
} }
@ -314,75 +289,6 @@ 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<Int, Int>? {
val c = vm.content
val p = vm.progress
fun tally(lessons: List<J>, 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.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.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)
Route.Cs4Map -> tally(c.cs4, p::cs4Done)
else -> null
}
}
private fun subjectSum(vm: AppViewModel, s: J): ProgressSum { private fun subjectSum(vm: AppViewModel, s: J): ProgressSum {
val sum = ProgressSum() val sum = ProgressSum()
when (s.str("target")) { when (s.str("target")) {
@ -442,34 +348,6 @@ private fun subjectSum(vm: AppViewModel, s: J): ProgressSum {
vm.content.fyz3.count { vm.progress.fyz3Done(it.int("id")) } + vm.content.fyz3.count { vm.progress.fyz3Done(it.int("id")) } +
vm.content.fyz4.count { vm.progress.fyz4Done(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")) } +
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")) }
}
"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")) }
}
"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" -> { "czechmap" -> {
val litDone = vm.content.lit.count { vm.progress.litDone(it.int("id")) } + val litDone = vm.content.lit.count { vm.progress.litDone(it.int("id")) } +
vm.content.lit2.count { vm.progress.lit2Done(it.int("id")) } + vm.content.lit2.count { vm.progress.lit2Done(it.int("id")) } +
@ -771,86 +649,6 @@ private fun buildSearch(vm: AppViewModel): List<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_lesson"), "fyz4unit${l.int("id")}", extra = "fyzika")
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "fyz4ex${l.int("id")}") 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")
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")
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("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("Chemie"), vm.tr("search_lesson"), "chemmap", extra = "chemie")
add(vm.tr("Biologie"), vm.tr("search_lesson"), "biomap", extra = "biologie") add(vm.tr("Biologie"), vm.tr("search_lesson"), "biomap", extra = "biologie")
vm.content.chem.forEach { l -> vm.content.chem.forEach { l ->
@ -947,10 +745,10 @@ fun NetYearsScreen(vm: AppViewModel) {
DetailScaffold(vm, vm.tr("net_years_title"), vm.tr("net_years_sub")) { inner -> DetailScaffold(vm, vm.tr("net_years_title"), vm.tr("net_years_sub")) { inner ->
LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) {
item { item {
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_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 = { LeadingBadge("2") }, progress = routeProgress(vm, Route.Net2Map)) { vm.go(Route.Net2Map) } 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 = { LeadingBadge("3") }, progress = routeProgress(vm, Route.Net3Map)) { vm.go(Route.Net3Map) } 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 = { LeadingBadge("4") }, progress = routeProgress(vm, Route.Net4Map)) { vm.go(Route.Net4Map) } NavRow(title = vm.tr("net_year4"), subtitle = vm.tr("net4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Net4Map) }
} }
} }
} }
@ -961,10 +759,10 @@ fun HwYearsScreen(vm: AppViewModel) {
DetailScaffold(vm, vm.tr("Technické vybavení"), vm.tr("hw_years_sub")) { inner -> DetailScaffold(vm, vm.tr("Technické vybavení"), vm.tr("hw_years_sub")) { inner ->
LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) {
item { item {
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_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 = { LeadingBadge("2") }, progress = routeProgress(vm, Route.Hw2Map)) { vm.go(Route.Hw2Map) } 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 = { LeadingBadge("3") }, progress = routeProgress(vm, Route.Hw3Map)) { vm.go(Route.Hw3Map) } 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 = { LeadingBadge("4") }, progress = routeProgress(vm, Route.Hw4Map)) { vm.go(Route.Hw4Map) } NavRow(title = vm.tr("hw_year4"), subtitle = vm.tr("hw4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Hw4Map) }
} }
} }
} }
@ -975,7 +773,7 @@ fun DeHomeScreen(vm: AppViewModel) {
DetailScaffold(vm, vm.tr("Deutsch"), vm.tr("de_home_sub")) { inner -> DetailScaffold(vm, vm.tr("Deutsch"), vm.tr("de_home_sub")) { inner ->
LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) {
item { item {
NavRow(title = vm.tr("de_book_title"), subtitle = vm.tr("de_book_sub"), leading = { LeadingBadge("U") }, progress = routeProgress(vm, Route.Roadmap)) { NavRow(title = vm.tr("de_book_title"), subtitle = vm.tr("de_book_sub"), leading = { Text("U", style = MaterialTheme.typography.titleMedium) }) {
vm.go(Route.Roadmap) vm.go(Route.Roadmap)
} }
} }
@ -984,8 +782,7 @@ fun DeHomeScreen(vm: AppViewModel) {
NavRow( NavRow(
title = vm.tr("de_year$year"), title = vm.tr("de_year$year"),
subtitle = vm.tr("de_y${year}_sub"), subtitle = vm.tr("de_y${year}_sub"),
leading = { LeadingBadge("$year") }, leading = { Text("$year.", style = MaterialTheme.typography.titleMedium) },
progress = routeProgress(vm, Route.DeMap(year)),
) { vm.go(Route.DeMap(year)) } ) { vm.go(Route.DeMap(year)) }
} }
} }
@ -1001,8 +798,7 @@ fun EnYearsScreen(vm: AppViewModel) {
NavRow( NavRow(
title = vm.tr("en_year$year"), title = vm.tr("en_year$year"),
subtitle = vm.tr("en_y${year}_sub"), subtitle = vm.tr("en_y${year}_sub"),
leading = { LeadingBadge("$year") }, leading = { Text("$year.", style = MaterialTheme.typography.titleMedium) },
progress = routeProgress(vm, Route.EnMap(year)),
) { vm.go(Route.EnMap(year)) } ) { vm.go(Route.EnMap(year)) }
} }
} }
@ -1014,10 +810,10 @@ fun OnYearsScreen(vm: AppViewModel) {
DetailScaffold(vm, vm.tr("Občanská nauka"), vm.tr("on_years_sub")) { inner -> DetailScaffold(vm, vm.tr("Občanská nauka"), vm.tr("on_years_sub")) { inner ->
LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) {
item { item {
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_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 = { LeadingBadge("2") }, progress = routeProgress(vm, Route.On2Map)) { vm.go(Route.On2Map) } 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 = { LeadingBadge("3") }, progress = routeProgress(vm, Route.On3Map)) { vm.go(Route.On3Map) } 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 = { LeadingBadge("4") }, progress = routeProgress(vm, Route.On4Map)) { vm.go(Route.On4Map) } NavRow(title = vm.tr("on_year4"), subtitle = vm.tr("on4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.On4Map) }
} }
} }
} }
@ -1028,10 +824,10 @@ fun LitYearsScreen(vm: AppViewModel) {
DetailScaffold(vm, vm.tr("Literatura"), vm.tr("lit_years_sub")) { inner -> DetailScaffold(vm, vm.tr("Literatura"), vm.tr("lit_years_sub")) { inner ->
LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) {
item { item {
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_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 = { LeadingBadge("2") }, progress = routeProgress(vm, Route.Lit2Map)) { vm.go(Route.Lit2Map) } 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 = { LeadingBadge("3") }, progress = routeProgress(vm, Route.Lit3Map)) { vm.go(Route.Lit3Map) } 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 = { LeadingBadge("4") }, progress = routeProgress(vm, Route.Lit4Map)) { vm.go(Route.Lit4Map) } NavRow(title = vm.tr("lit_year4"), subtitle = vm.tr("lit4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Lit4Map) }
} }
} }
} }
@ -1042,67 +838,11 @@ fun MatYearsScreen(vm: AppViewModel) {
DetailScaffold(vm, vm.tr("Matematika"), vm.tr("mat_years_sub")) { inner -> DetailScaffold(vm, vm.tr("Matematika"), vm.tr("mat_years_sub")) { inner ->
LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) {
item { item {
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("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 = { LeadingBadge("1") }, progress = routeProgress(vm, Route.MatMap)) { vm.go(Route.MatMap) } 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 = { LeadingBadge("2") }, progress = routeProgress(vm, Route.Mat2Map)) { vm.go(Route.Mat2Map) } 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 = { LeadingBadge("3") }, progress = routeProgress(vm, Route.Mat3Map)) { vm.go(Route.Mat3Map) } 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 = { LeadingBadge("4") }, progress = routeProgress(vm, Route.Mat4Map)) { vm.go(Route.Mat4Map) } NavRow(title = vm.tr("mat_year4"), subtitle = vm.tr("mat4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Mat4Map) }
}
}
}
}
@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 = { 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) }
}
}
}
}
@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 ->
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 ->
LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) {
item {
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) }
} }
} }
} }
@ -1113,10 +853,10 @@ fun FyzYearsScreen(vm: AppViewModel) {
DetailScaffold(vm, vm.tr("Fyzika"), vm.tr("fyz_years_sub")) { inner -> DetailScaffold(vm, vm.tr("Fyzika"), vm.tr("fyz_years_sub")) { inner ->
LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) {
item { item {
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_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 = { LeadingBadge("2") }, progress = routeProgress(vm, Route.Fyz2Map)) { vm.go(Route.Fyz2Map) } 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 = { LeadingBadge("3") }, progress = routeProgress(vm, Route.Fyz3Map)) { vm.go(Route.Fyz3Map) } 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 = { LeadingBadge("4") }, progress = routeProgress(vm, Route.Fyz4Map)) { vm.go(Route.Fyz4Map) } NavRow(title = vm.tr("fyz_year4"), subtitle = vm.tr("fyz4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Fyz4Map) }
} }
} }
} }
@ -1130,17 +870,11 @@ fun SciMapScreen(vm: AppViewModel) {
NavRow( NavRow(
title = vm.tr("Chemie"), title = vm.tr("Chemie"),
subtitle = vm.tr("chem_sub"), subtitle = vm.tr("chem_sub"),
leading = { LeadingIcon(Icons.Filled.Science) }, leading = { Icon(Icons.Filled.Science, null, tint = MaterialTheme.colorScheme.primary) },
progress = routeProgress(vm, Route.ChemMap),
) { vm.go(Route.ChemMap) } ) { vm.go(Route.ChemMap) }
} }
item { item {
NavRow( NavRow(title = vm.tr("Biologie"), subtitle = vm.tr("bio_sub")) { vm.go(Route.BioMap) }
title = vm.tr("Biologie"),
subtitle = vm.tr("bio_sub"),
leading = { LeadingIcon(Icons.Filled.Eco) },
progress = routeProgress(vm, Route.BioMap),
) { vm.go(Route.BioMap) }
} }
} }
} }
@ -1151,26 +885,20 @@ fun CzechMapScreen(vm: AppViewModel) {
DetailScaffold(vm, vm.tr("Český jazyk a literatura"), vm.tr("czech_sub")) { inner -> DetailScaffold(vm, vm.tr("Český jazyk a literatura"), vm.tr("czech_sub")) { inner ->
LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) {
item { item {
NavRow( NavRow(title = vm.tr("Literatura"), subtitle = vm.tr("lit_years_sub")) { vm.go(Route.LitYears) }
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 { item {
NavRow( NavRow(
title = vm.tr("Mluvnice"), title = vm.tr("Mluvnice"),
subtitle = vm.tr("mluv_sub"), subtitle = vm.tr("mluv_sub"),
leading = { LeadingIcon(Icons.Filled.FormatListNumbered) }, leading = { Icon(Icons.Filled.FormatListNumbered, null, tint = MaterialTheme.colorScheme.primary) },
progress = routeProgress(vm, Route.Mluvnice),
) { vm.go(Route.Mluvnice) } ) { vm.go(Route.Mluvnice) }
} }
item { item {
NavRow( NavRow(
title = vm.tr("Maturitní četba"), title = vm.tr("Maturitní četba"),
subtitle = vm.tr("reading_sub"), subtitle = vm.tr("reading_sub"),
leading = { LeadingIcon(Icons.AutoMirrored.Filled.MenuBook) }, leading = { Icon(Icons.AutoMirrored.Filled.MenuBook, null, tint = MaterialTheme.colorScheme.primary) },
) { vm.go(Route.ReadingList) } ) { vm.go(Route.ReadingList) }
} }
} }
@ -1233,52 +961,6 @@ fun BookScreen(vm: AppViewModel, id: String) {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Lesson slides // 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) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun SlidesScreen( fun SlidesScreen(
@ -1329,13 +1011,52 @@ fun SlidesScreen(
}, },
) { inner -> ) { inner ->
HorizontalPager(state = pager, modifier = Modifier.fillMaxSize().padding(inner)) { page -> HorizontalPager(state = pager, modifier = Modifier.fillMaxSize().padding(inner)) { page ->
val slide = slides[page]
Column( Column(
Modifier Modifier
.fillMaxSize() .fillMaxSize()
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.padding(horizontal = 16.dp, vertical = 12.dp), .padding(horizontal = 16.dp, vertical = 12.dp),
) { ) {
SlideContent(slides[page]) 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))
}
}
Spacer(Modifier.height(24.dp)) Spacer(Modifier.height(24.dp))
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

BIN
assets/germanlection.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

BIN
assets/subjectsscreen.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
assets/welcomescreen.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View file

@ -18,7 +18,7 @@ Skript vypíše počet lekcí, nebo chyby s cestou a číslem řádku
| Složka | Obsah | | Složka | Obsah |
|---|---| |---|---|
| `pocitacove-site/`, `technicke-vybaveni/`, `technicka-grafika/`, `prezentacni-grafika/`, `programove-vybaveni/`, `programovani/`, `obcanska-nauka/`, `fyzika/` | `N-rocnik/`, v nich lekce | | `pocitacove-site/`, `technicke-vybaveni/`, `obcanska-nauka/`, `fyzika/` | `N-rocnik/`, v nich lekce |
| `prirodni-vedy/chemie`, `prirodni-vedy/biologie` | lekce | | `prirodni-vedy/chemie`, `prirodni-vedy/biologie` | lekce |
| `cestina/literatura/N-rocnik` | lekce literatury | | `cestina/literatura/N-rocnik` | lekce literatury |
| `cestina/mluvnice` | cvičení z mluvnice | | `cestina/mluvnice` | cvičení z mluvnice |
@ -97,7 +97,7 @@ a smažte `puvodni`.
## Běžná lekce ## Běžná lekce
Sítě, hardware, technická grafika, prezentační grafika, programové vybavení, občanka, literatura, chemie, biologie, fyzika. Sítě, hardware, občanka, literatura, chemie, biologie, fyzika.
```md ```md
--- ---

View file

@ -5,30 +5,6 @@
# cs: bullet # cs: bullet
# en: 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
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í
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
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 --- 2026-10-07
cs: Sítě a technické vybavení: doplněné 2. až 4. ročníky, v každém 8 lekcí 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 cs: Výklad a kvíz od VLAN a sestavení počítače po maturitu

View file

@ -11,7 +11,7 @@
| Matematika | Matematika | Mathematics | function | matyears | ano | | Matematika | Matematika | Mathematics | function | matyears | ano |
| Fyzika | Fyzika | Physics | bolt | fyzyears | 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 | | 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 | | Technická grafika | Technická grafika | Technical Drawing | lock | | ne |
| Prezentační grafika | Prezentační grafika | Presentation Graphics | brush | pgyears | ano | | Prezentační grafika | Prezentační grafika | Presentation Graphics | lock | | ne |
| Programování | Programování | Programming | code | csyears | ano | | Programování | Programování | Programming | lock | | ne |
| Programové vybavení | Programové vybavení | Software | apps | swyears | ano | | Programové vybavení | Programové vybavení | Software | lock | | ne |

View file

@ -40,7 +40,7 @@
|---|---|---| |---|---|---|
| welcome_kicker | Graduately | Graduately | | welcome_kicker | Graduately | Graduately |
| welcome_title | Maturita?\nTo dáme. | Maturita?\nWe've got this. | | 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é studenty ze SSŠVT. | Exam practice in one window. Czech and English, written by students from SSŠVT. | | 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_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_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_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. | | 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. |
@ -117,7 +117,6 @@
| search_page | Stránka | Page | | search_page | Stránka | Page |
| search_home | Úvod | Welcome | | search_home | Úvod | Welcome |
| subjects_title | Předměty | Subjects | | 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. | | 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 # Čeština
@ -230,73 +229,6 @@
| fyz3_sub | Elektřina a magnetismus. | Electricity and magnetism. | | 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. | | 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. |
# 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é 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 |
|---|---|---|
| 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_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. |
# Angličtina a němčina # Angličtina a němčina
| Klíč | Česky | Anglicky | | Klíč | Česky | Anglicky |

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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í.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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ů.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
nazev: Masks
popis: A mask hides, it does not delete. Black conceals, white reveals.
nazev_cviceni: Quiz: masks
nadpis_kvizu: Read the mask
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
nazev: Smart objects and filters
popis: Blur and sharpening you can still change.
nazev_cviceni: Quiz: filters
nadpis_kvizu: Fill in the filter values
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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á.

View file

@ -1,6 +0,0 @@
---
nazev: Titles
popis: An opening and a closing, four seconds each.
nazev_cviceni: Quiz: titles
nadpis_kvizu: Fill in the time and the size
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
nazev: Sound
popis: Speech, music under it and a one-second fade.
nazev_cviceni: Quiz: sound
nadpis_kvizu: Set the levels
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
nazev: Colour in video
popis: Exposure first, then contrast, saturation last.
nazev_cviceni: Quiz: video colour
nadpis_kvizu: Put the steps in order
---

View file

@ -1,79 +0,0 @@
---
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á.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
nazev: Keyframes
popis: The logo arrives in one second, and the motion eases.
nazev_cviceni: Quiz: keyframes
nadpis_kvizu: Fill in the times
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,6 +0,0 @@
---
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
---

View file

@ -1,79 +0,0 @@
---
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.

View file

@ -1,4 +0,0 @@
---
nazev: Hello, world
popis: The first program: writing to the console.
---

View file

@ -1,42 +0,0 @@
---
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#");
```

View file

@ -1,4 +0,0 @@
---
nazev: Comments
popis: Notes the program does not run.
---

View file

@ -1,35 +0,0 @@
---
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");
*/
```

View file

@ -1,4 +0,0 @@
---
nazev: Variables
popis: A named value you can change.
---

View file

@ -1,36 +0,0 @@
---
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");
```

View file

@ -1,4 +0,0 @@
---
nazev: Data types
popis: int, double, bool, char and string.
---

View file

@ -1,40 +0,0 @@
---
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);
```

View file

@ -1,4 +0,0 @@
---
nazev: Type conversion
popis: From double to int and from text to a number.
---

View file

@ -1,36 +0,0 @@
---
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);
```

View file

@ -1,4 +0,0 @@
---
nazev: Operators
popis: Arithmetic, remainder, comparison and logic.
---

View file

@ -1,37 +0,0 @@
---
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);
```

Some files were not shown because too many files have changed in this diff Show more