From 0cc795cca89f0af6514f05b12cfad6603b7983cd Mon Sep 17 00:00:00 2001 From: OrangeX4 <318483724@qq.com> Date: Sat, 6 Apr 2024 17:54:31 +0800 Subject: [PATCH] feat: support numbering for sections and subsections --- changelog.md | 3 ++ examples/example.typ | 65 +++++++++++++++++++++++++++++++++++++++++-- slide.typ | 28 +++++++++++++++++-- themes/aqua.typ | 15 ++++------ themes/dewdrop.typ | 3 +- themes/metropolis.typ | 6 ++-- themes/simple.typ | 15 +++++----- themes/university.typ | 10 +++---- utils/states.typ | 33 +++++++++++++++++++++- 9 files changed, 146 insertions(+), 32 deletions(-) diff --git a/changelog.md b/changelog.md index 70c552d9a..dd3f82849 100644 --- a/changelog.md +++ b/changelog.md @@ -6,9 +6,12 @@ sidebar_position: 14 ## v0.3.4 +- **fix:** make nested includes work correctly. - **refactor:** remove `self.padding` and add `self.full-header` `self.full-footer` config. - **feat:** support `#footnote[]` for all themes (without frozen numbering). - **feat:** access subslide and repeat in footer and header by `self => self.subslide`. +- **feat:** support numbered theorem environments by [ctheorems](https://typst.app/universe/package/ctheorems). +- **feat:** support numbering for sections and subsections. ## v0.3.3 diff --git a/examples/example.typ b/examples/example.typ index 4e33e2e77..1ed8a8dbc 100644 --- a/examples/example.typ +++ b/examples/example.typ @@ -1,16 +1,20 @@ #import "../lib.typ": * -#import "@preview/cetz:0.2.1" -#import "@preview/fletcher:0.4.2" as fletcher: node, edge +#import "@preview/cetz:0.2.2" +#import "@preview/fletcher:0.4.3" as fletcher: node, edge +#import "@preview/ctheorems:1.1.2": * // cetz and fletcher bindings for touying #let cetz-canvas = touying-reducer.with(reduce: cetz.canvas, cover: cetz.draw.hide.with(bounds: true)) -#let fletcher-diagram = touying-reducer.with(reduce: (arr, ..args) => fletcher.diagram(..args, ..arr)) +#let fletcher-diagram = touying-reducer.with(reduce: fletcher.diagram, cover: fletcher.hide) // Register university theme // You can remove the theme registration or replace other themes // it can still work normally #let s = themes.university.register(aspect-ratio: "16-9") +// Set the numbering of section and subsection +#let s = (s.methods.numbering)(self: s, section: "1.", "1.1") + // Global information configuration #let s = (s.methods.info)( self: s, @@ -39,6 +43,19 @@ ), )) +// Theroems configuration by ctheorems +#show: thmrules.with(qed-symbol: $square$) +#let theorem = thmbox("theorem", "Theorem", fill: rgb("#eeffee")) +#let corollary = thmplain( + "corollary", + "Corollary", + base: "theorem", + titlefmt: strong +) +#let definition = thmbox("definition", "Definition", inset: (x: 1.2em, top: 1em)) +#let example = thmplain("example", "Example").with(numbering: none) +#let proof = thmproof("proof", "Proof") + // Extract methods #let (init, slides, touying-outline, alert) = utils.methods(s) #show: init @@ -147,6 +164,48 @@ ] += Theroems + +== Prime numbers + +#definition[ + A natural number is called a #highlight[_prime number_] if it is greater + than 1 and cannot be written as the product of two smaller natural numbers. +] +#example[ + The numbers $2$, $3$, and $17$ are prime. + @cor_largest_prime shows that this list is not exhaustive! +] + +#theorem("Euclid")[ + There are infinitely many primes. +] +#proof[ + Suppose to the contrary that $p_1, p_2, dots, p_n$ is a finite enumeration + of all primes. Set $P = p_1 p_2 dots p_n$. Since $P + 1$ is not in our list, + it cannot be prime. Thus, some prime factor $p_j$ divides $P + 1$. Since + $p_j$ also divides $P$, it must divide the difference $(P + 1) - P = 1$, a + contradiction. +] + +#corollary[ + There is no largest prime number. +] +#corollary[ + There are infinitely many composite numbers. +] + +#theorem[ + There are arbitrarily long stretches of composite numbers. +] + +#proof[ + For any $n > 2$, consider $ + n! + 2, quad n! + 3, quad ..., quad n! + n #qedhere + $ +] + + = Others == Side-by-side diff --git a/slide.typ b/slide.typ index 8fd3f9beb..98ac2b6f5 100644 --- a/slide.typ +++ b/slide.typ @@ -439,10 +439,17 @@ // for speed up, do not parse the content if repeat is none if repeat == none { return { + let conts = bodies.map(it => { + if type(it) == function { + it(self) + } else { + it + } + }) header = _update-states(1) + header set page(..(self.page-args + (header: header, footer: footer))) setting( - page-preamble(1) + composer-with-side-by-side(..bodies) + page-preamble(1) + composer-with-side-by-side(..conts) ) } } @@ -638,6 +645,8 @@ // full header / footer full-header: true, full-footer: true, + // numbering + numbering: none, // datetime format datetime-format: auto, // register the methods @@ -709,6 +718,21 @@ self.datetime-format = format self }, + // numbering + numbering: (self: none, section: auto, subsection: auto, numbering) => { + if section == auto and subsection == auto { + self.numbering = numbering + return self + } + let section-numbering = if section == auto { numbering } else { section } + let subsection-numbering = if subsection == auto { numbering } else { subsection } + self.numbering = (..args) => if args.pos().len() == 1 { + states._typst-numbering(section-numbering, ..args) + } else { + states._typst-numbering(subsection-numbering, ..args) + } + self + }, // default init init: (self: none, body) => { // default text size @@ -718,7 +742,7 @@ }, // default outline touying-outline: (self: none, ..args) => { - states.touying-outline(..args) + states.touying-outline(self: self, ..args) }, appendix: (self: none) => { self.appendix = true diff --git a/themes/aqua.typ b/themes/aqua.typ index 60fcc6d43..060207e3a 100644 --- a/themes/aqua.typ +++ b/themes/aqua.typ @@ -70,7 +70,7 @@ { set par(leading: leading) set text(weight: "bold") - (self.methods.touying-outline)(self: self, enum-args: (numbering: "01", ..enum-args)) + (self.methods.touying-outline)(self: self, enum-args: (numbering: self.numbering, ..enum-args)) } ) ) @@ -93,7 +93,7 @@ text( fill: self.colors.primary, size: 166pt, - states.current-section-number(numbering: "01") + states.current-section-number(numbering: self.numbering) ) ), align( @@ -156,11 +156,7 @@ primary-lightest: rgb("#F2F4F8"), ) - self.aqua-title = { - states.current-section-number(numbering: "01") - [ ] - states.current-section-title - } + self.aqua-title = states.current-section-with-numbering self.aqua-footer = footer self.aqua-lang = lang self.aqua-background = (self) => { @@ -199,14 +195,14 @@ width: 100%, height: 1.8em, fill: self.colors.primary, - align(left + horizon, h(1.5em) + text(fill:white, self.aqua-title)) + align(left + horizon, h(1.5em) + text(fill:white, utils.call-or-display(self, self.aqua-title))) ) ) place(left + top, line(start: (30%, 0%), end: (27%, 100%), stroke: .5em + white)) } let footer(self) = { set text(size: 0.8em) - place(right, dx: -5%, utils.call-or-display(self, self.aqua-footer)) + place(right, dx: -5%, utils.call-or-display(self, utils.call-or-display(self, self.aqua-footer))) } self.page-args += ( paper: "presentation-" + aspect-ratio, @@ -218,6 +214,7 @@ set text(size: 20pt) body } + self.numbering = "01" self.methods.title-slide = title-slide self.methods.outline-slide = outline-slide self.methods.focus-slide = focus-slide diff --git a/themes/dewdrop.typ b/themes/dewdrop.typ index 94d395947..3ca76b68c 100644 --- a/themes/dewdrop.typ +++ b/themes/dewdrop.typ @@ -28,7 +28,7 @@ show heading: set text(fill: self.colors.primary) show: args.named().at("setting", default: body => body) if self.auto-heading-for-subsection and subsection != none { - heading(level: 1, subsection) + heading(level: 1, states.current-subsection-with-numbering(self)) } if self.auto-heading and title != none { heading(level: 2, title) @@ -284,6 +284,7 @@ )} else {( margin: (top: 2em, bottom: 2em, x: mini-slides.x), )} + self = (self.methods.numbering)(self: self, section: "1.", "1.1") // register methods self.methods.slide = slide self.methods.title-slide = title-slide diff --git a/themes/metropolis.typ b/themes/metropolis.typ index cb967e079..dfffb2a5f 100644 --- a/themes/metropolis.typ +++ b/themes/metropolis.typ @@ -85,7 +85,7 @@ set align(horizon) show: pad.with(20%) set text(size: 1.5em) - title + states.current-section-with-numbering(self) block(height: 2pt, width: 100%, spacing: 0pt, utils.call-or-display(self, self.m-progress-bar)) } (self.methods.touying-slide)(self: self, repeat: none, section: (title: title, short-title: short-title), content) @@ -114,7 +114,7 @@ #let register( self: s, aspect-ratio: "16-9", - header: states.current-section-title, + header: states.current-section-with-numbering, footer: [], footer-right: states.slide-counter.display() + " / " + states.last-slide-number, footer-progress: true, @@ -177,7 +177,7 @@ self.methods.focus-slide = focus-slide self.methods.slides = slides self.methods.touying-outline = (self: none, enum-args: (:), ..args) => { - states.touying-outline(enum-args: (tight: false,) + enum-args, ..args) + states.touying-outline(self: self, enum-args: (tight: false,) + enum-args, ..args) } self.methods.alert = (self: none, it) => text(fill: self.colors.secondary-light, it) self diff --git a/themes/simple.typ b/themes/simple.typ index 0267cc6f8..017e88bb1 100644 --- a/themes/simple.typ +++ b/themes/simple.typ @@ -17,10 +17,10 @@ }, ..args) } -#let centered-slide(self: none, section: none, ..args) = { +#let centered-slide(self: none, ..args) = { self = utils.empty-page(self, margin: none) - (self.methods.touying-slide)(self: self, repeat: none, section: section, ..args.named(), - align(center + horizon, if section != none { heading(level: 1, utils.unify-section(section).title) } + args.pos().sum(default: [])) + (self.methods.touying-slide)(self: self, repeat: none, ..args.named(), + align(center + horizon, args.pos().sum(default: [])) ) } @@ -29,7 +29,9 @@ } #let new-section-slide(self: none, section) = { - centered-slide(self: self, section: section) + self = utils.empty-page(self, margin: none) + (self.methods.touying-slide)(self: self, repeat: none, section: section, align(center + horizon, heading(level: 1, states.current-section-with-numbering(self))) + ) } #let focus-slide(self: none, background: auto, foreground: white, body) = { @@ -62,10 +64,7 @@ self.simple-footer-right = footer-right self.auto-heading = true // set page - let header = locate(loc => { - let sections = states.sections-state.at(loc) - deco-format(sections.last().title) - }) + let header = self => deco-format(states.current-section-with-numbering(self)) let footer(self) = deco-format(self.simple-footer + h(1fr) + self.simple-footer-right) self.page-args += ( paper: "presentation-" + aspect-ratio, diff --git a/themes/university.typ b/themes/university.typ index f798b8b3c..108986ce1 100644 --- a/themes/university.typ +++ b/themes/university.typ @@ -88,11 +88,11 @@ #let new-section-slide(self: none, short-title: auto, title) = { self = utils.empty-page(self) - let content = { + let content(self) = { set align(horizon) show: pad.with(20%) set text(size: 1.5em, fill: self.colors.primary, weight: "bold") - title + states.current-section-with-numbering(self) v(-.5em) block(height: 2pt, width: 100%, spacing: 0pt, utils.call-or-display(self, self.uni-progress-bar)) } @@ -230,7 +230,7 @@ align(top + left, text(fill: self.colors.primary, weight: "bold", size: 1.2em, self.uni-title)), [], if self.uni-display-current-section { - align(top + right, text(fill: self.colors.primary.lighten(65%), states.current-section-title)) + align(top + right, text(fill: self.colors.primary.lighten(65%), states.current-section-with-numbering(self))) } ), text(fill: self.colors.primary.lighten(65%), size: .8em, self.uni-subtitle) @@ -262,7 +262,7 @@ footer: footer, header-ascent: 0em, footer-descent: 0em, - margin: (top: 2.5em, bottom: 1em, x: 2em), + margin: (top: 2.5em, bottom: 1.25em, x: 2em), ) // register methods self.methods.slide = slide @@ -273,7 +273,7 @@ self.methods.matrix-slide = matrix-slide self.methods.slides = slides self.methods.touying-outline = (self: none, enum-args: (:), ..args) => { - states.touying-outline(enum-args: (tight: false,) + enum-args, ..args) + states.touying-outline(self: self, enum-args: (tight: false,) + enum-args, ..args) } self.methods.alert = (self: none, it) => text(fill: self.colors.primary, it) self.methods.init = (self: none, body) => { diff --git a/utils/states.typ b/utils/states.typ index bc62be5db..1a6914905 100644 --- a/utils/states.typ +++ b/utils/states.typ @@ -51,7 +51,11 @@ callback(sections-state.final(loc)) }) -#let touying-outline(enum-args: (:), padding: 0pt) = touying-final-sections(sections => { +#let touying-outline(self: none, enum-args: (:), padding: 0pt) = touying-final-sections(sections => { + let enum-args = (full: true) + enum-args + if self != none and self.numbering != none { + enum-args = (numbering: self.numbering) + enum-args + } pad(padding, enum( ..enum-args, ..sections.filter(section => section.loc != none) @@ -78,6 +82,33 @@ } }) +#let current-section-with-numbering(self, ignore-zero: true) = locate(loc => { + let sections = sections-state.at(loc) + if self.numbering != none and (not ignore-zero or sections.len() - 1 != 0) { + _typst-numbering(self.numbering, sections.len() - 1) + [ ] + } + sections.last().title +}) + +#let current-subsection-number(numbering: "1.1", ignore-zero: true) = locate(loc => { + let sections = sections-state.at(loc) + let subsections = sections.last().children + if (not ignore-zero or sections.len() - 1 != 0) and (not ignore-zero or subsections.len() - 1 != 0) { + _typst-numbering(numbering, sections.len() - 1, subsections.len() - 1) + } +}) + +#let current-subsection-with-numbering(self, ignore-zero: true) = locate(loc => { + let sections = sections-state.at(loc) + let subsections = sections.last().children + if self.numbering != none and (not ignore-zero or sections.len() - 1 != 0) and (not ignore-zero or subsections.len() - 1 != 0) { + _typst-numbering(self.numbering, sections.len() - 1, subsections.len() - 1) + [ ] + } + subsections.last().title +}) + #let touying-progress-with-sections(callback) = locate(loc => { callback(( current-sections: sections-state.at(loc),