% \iffalse meta-comment % % File: ltx-talk-frame.dtx Copyright (C) 2023-2026 Joseph Wright % % It may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this % license or (at your option) any later version. The latest version % of this license is in the file % % https://www.latex-project.org/lppl.txt % % This file is part of the "ltx-talk bundle" (The Work in LPPL) % and all files in that bundle must be distributed together. % % The released version of this bundle is available from CTAN. % % ----------------------------------------------------------------------- % % The development version of the bundle can be found at % % https://github.com/josephwright/ltx-talk % % for those people who are interested. % % ----------------------------------------------------------------------- % %<*driver> \documentclass{l3doc} % Additional commands needed in this source \NewDocumentCommand\email{m}{\href{mailto:#1}{\nolinkurl{#1}}} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % ^^A As we are dealing with a class, this has to be done manually % \def\filedate{2026-09-30} % \def\fileversion{v0.6.8} % % \title{^^A % \pkg{ltx-talk-frame} -- The structure of frames^^A % \thanks{This file describes \fileversion, % last revised \filedate.}^^A % } % % \author{^^A % Joseph Wright^^A % \thanks{^^A % E-mail: \email{joseph@texdev.net}^^A % }^^A % } % % \date{Released \filedate} % % \maketitle % % \begin{documentation} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{ltx-talk-frame} implementation} % % Start the \pkg{DocStrip} guards. % \begin{macrocode} %<*class> % \end{macrocode} % % Identify the internal prefix. % \begin{macrocode} %<@@=talk> % \end{macrocode} % % \subsection{Slides in frames} % % Currently, each slide in a frame will produce a separate page in the output % (unless the slide is suppressed entirely). Material is then hidden on some % pages by using opacity. An alternative approach would be to use Optional % Content Groups to have a similar effect on one page per frame. However, % whilst that would be relatively clear for appear/disappear effects, it would % be much less straight-forward for partial transparency, \foreign{etc.}, plus % would depend more heavily on viewer support. At a future stage we may wish to % revisit this. % % \begin{variable}{\g_@@_slide_continue_bool, \l_@@_slide_continue_bool} % Tracks whether the frame continues after the current slide. % \begin{macrocode} \bool_new:N \g_@@_slide_continue_bool \bool_new:N \l_@@_slide_continue_bool % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_break_box, \l_@@_slide_box} % \begin{macrocode} \box_new:N \g_@@_break_box \box_new:N \l_@@_break_box % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_slide_box} % \begin{macrocode} \box_new:N \l_@@_slide_box % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_slide_int, \c@slide, \theslide} % The slide number inside the current frame: needed to know which overlays % are active. We also provide \LaTeX{} counter-style access. % \begin{macrocode} \int_new:N \g_@@_slide_int \cs_new_eq:NN \c@slide \g_@@_slide_int \cs_new:Npn \theslide { \@arabic \c@slide } % \end{macrocode} % \end{variable} % % Required to know which is the last slide in a frame for tagging. % \begin{macrocode} \property_new:nnnn { slides } { now } { 1 } { \int_use:N \g_@@_slide_int } % \end{macrocode} % % \begin{macro}{\@@_slide:nn, \@@_slide_auxi:nn} % \begin{macro}{\@@_slide_auxii:n} % \begin{macro}{\@@_slide_auxiii:} % \begin{macro}{\@@_slide_auxiv:n} % Each slide is parsed inside simple set up, the only complexity being if we % are handling fragile frames. There, all \cs{obeyedline} in the grabbed % tokens need to be turned back into |^^M| before rescanning: this ensures % that any verbatim grabbing in the frame still works. The strange business % with setting the continuation boolean is needed as otherwise we get an % infinite loop if there is an overlay specification for the frame itself. % Tagging should not of itself force slide continuation, so the global % boolean is reset for the tagged slide. % \begin{macrocode} \cs_new_protected:Npn \@@_slide:nn #1#2 { \group_begin: \tl_set:Ne \l_@@_tmp_tl { \property_ref:ee { frame . \int_use:N \g_@@_frame_int } { slides } } \str_if_eq:VnTF \l_@@_frame_tagging_str { n } { \str_set:NV \l_@@_frame_tagging_str \l_@@_tmp_tl } { \str_replace_all:NnV \l_@@_frame_tagging_str { ,n } \l_@@_tmp_tl \str_replace_all:NnV \l_@@_frame_tagging_str { ,~n } \l_@@_tmp_tl } \int_gzero:N \g_@@_slide_int \RenewCommandCopy \frame \@@_latexe_frame:n \bool_do_while:Nn \g_@@_slide_continue_bool { \@@_slide_auxi:nn {#1} {#2} } \group_end: } \cs_new_protected:Npn \@@_slide_auxi:nn #1#2 { \int_gincr:N \g_@@_slide_int \bool_gset_false:N \g_@@_slide_continue_bool \@@_if_overlay:nT {#1} { \@@_slide_auxii:n {#2} } } % \end{macrocode} % The business end of the slide starts in vertical mode, and opens the % surrounding vertical box. Immediately inside is a horizontal box % which will contain any tagging-related whatsits. Once that is done, % back to vertical mode for the user content. The use of \texttt{:Nw} % rather than \texttt{:n} boxes here is to allow the the split of % the functions into \enquote{before} and \enquote{after} the content is % typeset. % \begin{macrocode} \cs_new_protected:Npn \@@_slide_auxii:n #1 { \@@_slide_init: \vbox_set:Nw \l_@@_slide_box \hbox_set:Nw \l_@@_slide_box \bool_if:NTF \g_@@_frame_tag_bool { \@@_slide_tag_on_begin: } { \@@_slide_tag_off_begin: } \vbox_set:Nw \l_@@_slide_box \bool_if:NTF \l_@@_frame_verb_bool { \@@_slide_auxiv:n } { \use:n } {#1} \tl_use:N \g_@@_onslide_tl \vbox_set_end: \bool_lazy_and:nnTF { \l_@@_frame_break_bool } { \dim_compare_p:nNn { \box_ht:N \l_@@_slide_box } > \textheight } { \@@_slide_break: } { \@@_slide_fill: \@@_slide_auxiii: } } \cs_new_protected:Npn \@@_slide_auxiii: { \box_use_drop:N \l_@@_slide_box \par \bool_if:NTF \g_@@_frame_tag_bool { \@@_slide_tag_on_end: } { \@@_slide_tag_off_end: } \hbox_set_end: \box_use_drop:N \l_@@_slide_box \vbox_set_end: \bool_if:NT \g_@@_slide_continue_bool { \@@_cnt_restore: } \@@_slide_output: } \cs_new_protected:Npn \@@_slide_auxiv:n #1 { \group_begin: \cs_set:Npn \obeyedline { ^^J } \use:e { \group_end: \tl_retokenize:n {#1} } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \begin{macro}{\@@_slide_init:} % Collects up some key initialization in one place; we set up a single % tagging boolean here, which needs some shuffling of the global overlay % state. % \begin{macrocode} \cs_new_protected:Npn \@@_slide_init: { \tl_gclear:N \g_@@_onslide_tl \int_gzero:N \g_@@_pauses_int \tl_gclear:N \g_@@_frame_title_tl \tl_gclear:N \g_@@_frame_subtitle_tl \box_gclear:N \g_@@_footnote_box \@@_cnt_save: \bool_set_eq:NN \l_@@_slide_continue_bool \g_@@_slide_continue_bool \@@_if_overlay:VTF \l_@@_frame_tagging_str { \bool_gset_true:N \g_@@_frame_tag_bool } { \bool_gset_false:N \g_@@_frame_tag_bool } \bool_gset_eq:NN \g_@@_slide_continue_bool \l_@@_slide_continue_bool } % \end{macrocode} % \end{macro} % \begin{macro}{\@@_slide_fill:} % Once the horizontal box carrying the tagging is closed, the content % inside it is rigid, and so any stretchable glue in it (\cs{vfill}, the % |stretch| alignment) would stay at its natural size. So a slide that % is not being broken is re-set here, still inside that box, to the height % available for it and with the alignment glue around it. Footnotes are % added below the content in \cs{@@_slide_output:}, and so their height is % not available: the standard \cs{footnoterule} takes up no net height, and % the depth of the last footnote becomes the depth of the slide. (The depth % of the last line of content needs no allowance either: the alignment glue % ends the box, and so that depth is part of its height.) % \begin{macrocode} \cs_new_protected:Npn \@@_slide_fill: { \vbox_set_to_ht:Nnn \l_@@_slide_box { \textheight - \box_ht:N \g_@@_footnote_box } { \use:c { @@_slide_align_ \l_@@_frame_alignment_tl :n } { \vbox_unpack_drop:N \l_@@_slide_box } } } % \end{macrocode} % \end{macro} % \begin{macro}{\@@_slide_break:} % Breaking a slide requires we get the content out of the multiple layers % required for tagging. That means first saving the inner box then splitting % before closing up and finalising the first slide as normal. We then need % a simple loop to chop up the rest of the content: we keep the box nesting % the same for each slide in the output. % \begin{macrocode} \cs_new_protected:Npn \@@_slide_break: { \box_set_eq:NN \l_@@_break_box \l_@@_slide_box \vbox_set_split_to_ht:NNn \l_@@_slide_box \l_@@_break_box { \fp_to_dim:n { \l_@@_frame_break_fp * \textheight } } \box_gset_eq:NN \g_@@_break_box \l_@@_break_box \@@_slide_auxiii: \box_set_eq:NN \l_@@_break_box \g_@@_break_box \bool_do_until:nn { \box_if_empty_p:N \l_@@_break_box } { \vbox_set_split_to_ht:NNn \l_@@_slide_box \l_@@_break_box { \fp_to_dim:n { \l_@@_frame_break_fp * \textheight } } \vbox_set:Nn \l_@@_slide_box { \hbox:n { \box_use_drop:N \l_@@_slide_box } } \@@_slide_output: } } % \end{macrocode} % \end{macro} % \begin{macro}{\@@_slide_output:} % Send the slide content to the main vertical list, after adding any % footnotes. % \begin{macrocode} \cs_new_protected:Npn \@@_slide_output: { \thispdfpagelabel { \int_use:N \g_@@_frame_int } \vbox_to_ht:nn { \textheight } { \use:c { @@_slide_align_ \l_@@_frame_alignment_tl :n } { \vbox_unpack_drop:N \l_@@_slide_box } \box_if_empty:NF \g_@@_footnote_box { \footnoterule \vbox_unpack_drop:N \g_@@_footnote_box } } \bool_if:NF \g_@@_slide_continue_bool { \clist_set_eq:NN \l_@@_tmp_clist \l_talk_frame_properties_clist \clist_put_left:Nn \l_@@_tmp_clist { slides } \clist_remove_duplicates:N \l_@@_tmp_clist \property_record:ee { frame . \int_use:N \g_@@_frame_int } { \l_@@_tmp_clist } } \clearpage } % \end{macrocode} % \end{macro} % % \begin{macro} % { % \@@_slide_align_bottom:n , % \@@_slide_align_center:n , % \@@_slide_align_stretch:n , % \@@_slide_align_top:n % } % A pretty standard abstraction: we make sure there are always two skips. % \begin{macrocode} \cs_new_protected:Npn \@@_slide_align_bottom:n #1 { \skip_vertical:n { 0pt~plus~1fil } #1 \skip_vertical:n { 0pt } } \cs_new_protected:Npn \@@_slide_align_center:n #1 { \skip_vertical:n { 0pt~plus~0.5fil } #1 \skip_vertical:n { 0pt~plus~0.5fil } } \cs_new_protected:Npn \@@_slide_align_stretch:n #1 { \skip_vertical:n { 0pt } #1 \skip_vertical:n { 0pt } } \cs_new_protected:Npn \@@_slide_align_top:n #1 { \skip_vertical:n { 0pt } #1 \skip_vertical:n { 0pt~plus~1fil } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_slide_tag_on_begin:, \@@_slide_tag_on_end:} % Wraps some content in tagging for a frame: we may have multiple of these % in one logical frame, but that is non-standard. % \begin{macrocode} \cs_new_protected:Npn \@@_slide_tag_on_begin: { \tag_struct_begin:n { tag = frame } \int_gset:Nn \g_@@_frame_struct_int { \tag_get:n { struct_num } } } \cs_new_protected:Npn \@@_slide_tag_on_end: { \tag_struct_end: } % \end{macrocode} % \end{macro} % \begin{macro}{\@@_slide_tag_off_begin:, \@@_slide_tag_off_end:} % The alternative: turn off tagging and suppress the function that would % tag the frame title. % \begin{macrocode} \cs_new_protected:Npn \@@_slide_tag_off_begin: { \tag_mc_begin:n { artifact } \tag_suspend:n { frame } } \cs_new_protected:Npn \@@_slide_tag_off_end: { \tag_resume:n { frame } \tag_mc_end: } % \end{macrocode} % \end{macro} % % \begin{variable}{\g_@@_frame_struct_int} % The tagging structure number for the slide: needed by the content placed % outside of the current box (for example the frame title). % \begin{macrocode} \int_new:N \g_@@_frame_struct_int % \end{macrocode} % \end{variable} % % \subsection{Counters} % % \begin{variable}{\g_@@_cnt_reset_seq} % As \cs{stepcounter}, \foreign{etc}., will increment at each overlay, there % is a need to save and reset. The list will be finalized at the end of the % preamble, so the data storage is created at that point. The starting point % is counters created before the class is loaded (other than those for lists, % which reset \enquote{naturally}). Other cases are handled by adding to % \cs{newcounter}. % \begin{macrocode} \seq_new:N \g_@@_cnt_reset_seq \seq_gset_from_clist:Nn \g_@@_cnt_reset_seq { equation , footnote , mpfootnote , parentequation } \seq_map_inline:Nn \g_@@_cnt_reset_seq { \int_new:c { g_@@_saved_ #1 _int } \int_gset_eq:cc { g_@@_saved_ #1 _int } { c@ #1 } } % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_cnt_save:, \@@_cnt_restore:} % A simple save-and-restore pair. % \begin{macrocode} \cs_new_protected:Npn \@@_cnt_save: { \seq_map_inline:Nn \g_@@_cnt_reset_seq { \int_gset_eq:cc { g_@@_saved_ ##1 _int } { c@ ##1 } } } \cs_new_protected:Npn \@@_cnt_restore: { \seq_map_inline:Nn \g_@@_cnt_reset_seq { \int_gset_eq:cc { c@ ##1 } { g_@@_saved_ ##1 _int } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@definecounter, \std@definecounter} % Track all counters for resetting. % \begin{macrocode} \cs_new_eq:NN \std@definecounter \@definecounter \cs_gset_protected:Npn \@definecounter #1 { \std@definecounter {#1} \int_new:c { g_@@_saved_ #1 _int } \seq_gput_right:Nn \g_@@_cnt_reset_seq {#1} } % \end{macrocode} % \end{macro} % % \subsection{Frame options} % % \begin{variable}{\l_@@_frame_alignment_tl} % \begin{macrocode} \tl_new:N \l_@@_frame_alignment_tl % \end{macrocode} % \end{variable} % % \begin{variable} % { % \l_@@_action_spec_str , % \l_@@_frame_break_bool , % \l_@@_frame_break_fp , % \l_@@_frame_name_str , % \l_@@_frame_suppl_bool , % \l_@@_frame_tagging_str % } % \begin{macrocode} \bool_new:N \l_@@_frame_suppl_bool \keys_define:nn { talk / frame } { action-spec .str_set:N = \l_@@_action_spec_str , auto-break .bool_set:N = \l_@@_frame_break_bool , auto-break-coverage .fp_set:N = \l_@@_frame_break_fp , label .meta:n = { name = {#1} } , name .str_set:N = \l_@@_frame_name_str , supplementary-frame .bool_set:N = \l_@@_frame_suppl_bool , tag-slides .str_set:N = \l_@@_frame_tagging_str , vertical-alignment .choices:nn = { bottom , center , stretch , top } { \tl_set_eq:NN \l_@@_frame_alignment_tl \l_keys_value_tl } } \keys_set:nn { talk / frame } { action-spec = , auto-break = false , auto-break-coverage = 0.85 , name = , supplementary-frame = false , tag-slides = n , vertical-alignment = center } % \end{macrocode} % \end{variable} % % \subsection{Tagging for headers} % % \begin{macro}{\@@_header_tag_begin:n, \@@_header_tag_begin:e} % \begin{macro}{\@@_header_tag_end:} % Generalized control for inserting material into the header area (which is % otherwise outside of tagging). % \begin{macrocode} \cs_new_protected:Npn \@@_header_tag_begin:n #1 { \tag_resume:n { header } \tag_mc_end: \tag_struct_begin:n {#1} \tag_mc_begin:n { } } \cs_generate_variant:Nn \@@_header_tag_begin:n { e } \cs_new_protected:Npn \@@_header_tag_end: { \tag_mc_end: \tag_struct_end: \tag_mc_begin:n { artifact } \tag_suspend:n { header } } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Wallpaper} % % \begin{variable} % { % \l_@@_footelem_left_skip , % \l_@@_footelem_right_skip , % \l_@@_footelem_color_tl , % \l_@@_footelem_font_tl % } % \begin{macrocode} \NewTemplateType { footer-element } { 1 } \DeclareTemplateInterface { footer-element } { talk } { 1 } { color : tokenlist , font : tokenlist = , left-hspace : length = 0em , right-hspace : length = 0em } \DeclareTemplateCode { footer-element } { talk } { 1 } { color = \l_@@_footelem_color_tl , font = \l_@@_footelem_font_tl , left-hspace = \l_@@_footelem_left_skip , right-hspace = \l_@@_footelem_right_skip } { \tl_if_empty:nF {#1} { \hspace { \l_@@_footelem_left_skip } \hbox:n { \tl_if_empty:NF \l_@@_footelem_color_tl { \color_select:V \l_@@_footelem_color_tl } \l_@@_footelem_font_tl #1 } \hspace { \l_@@_footelem_right_skip } } } \DeclareInstance { footer-element } { date } { talk } { } \DeclareInstance { footer-element } { author } { talk } { } \DeclareInstance { footer-element } { title } { talk } { } \DeclareInstance { footer-element } { subtitle } { talk } { } \DeclareInstance { footer-element } { institute } { talk } { } \DeclareInstance { footer-element } { framenumber } { talk } { } \DeclareInstance { footer-element } { totalframes } { talk } { } % \end{macrocode} % \end{variable} % % \begin{variable} % { % \l_@@_header_bg_tl , % \l_@@_header_fg_tl , % \l_@@_header_font_tl , % \l_@@_header_ht_dim , % \l_@@_header_left_skip , % \l_@@_header_frametitle_bool , % \l_@@_header_right_skip % } % Templates for the header area. The background always covers the full width, % but the text area may be narrower. The setup here aims to avoid repeated % kerns but also dealing with complex conditionals, hence we always move to % the edge of the paper first then adjust as required. % \begin{macrocode} \NewTemplateType { header } { 0 } \DeclareTemplateInterface { header } { talk } { 0 } { background-color : tokenlist, color : tokenlist = structure , font : tokenlist = \normalfont , height : length = \Gm@tmargin + \headsep , left-hspace : skip = \Gm@lmargin , print-frame-title : boolean = true , right-hspace : skip = \Gm@rmargin } \DeclareTemplateCode { header } { talk } { 0 } { background-color = \l_@@_header_bg_tl , color = \l_@@_header_fg_tl , font = \l_@@_header_font_tl , height = \l_@@_header_ht_dim , left-hspace = \l_@@_header_left_skip , print-frame-title = \l_@@_header_frametitle_bool , right-hspace = \l_@@_header_right_skip } { \noindent \@@_wallpaper_hrule:Nnn \l_@@_header_bg_tl { \l_@@_header_ht_dim - \headsep } { \headsep } \skip_horizontal:n { \l_@@_header_left_skip } \vbox_set_to_wd:Nnn \l_@@_tmp_box { \paperwidth - \l_@@_header_left_skip - \l_@@_header_right_skip } { \tl_if_empty:NF \l_@@_header_fg_tl { \color_select:V \l_@@_header_fg_tl } \l_@@_header_font_tl \bool_if:NT \l_@@_header_frametitle_bool { \ExpandArgs { nnV } \UseInstance { frametitle } { header } \g_@@_frame_title_tl } } \dim_compare:nNnT { \box_ht:N \l_@@_tmp_box + \box_dp:N \l_@@_tmp_box } > \l_@@_header_ht_dim { \msg_warning:nn { talk } { overheight-header } } \box_use_drop:N \l_@@_tmp_box \skip_horizontal:n { \l_@@_header_right_skip - \Gm@rmargin } } \DeclareInstance { header } { std } { talk } { } \AddToHook { begindocument } { \DeclareInstanceCopy { header } { wallpaper } { std } \EditInstance { header } { wallpaper } { print-frame-title = false } } % \end{macrocode} % \end{variable} % \begin{variable} % { % \l_@@_footer_bg_tl , % \l_@@_footer_fg_tl , % \l_@@_footer_font_tl , % \l_@@_footer_order_clist , % \l_@@_footer_sep_tl , % \l_@@_footer_font_tl , % \l_@@_footer_left_skip , % \l_@@_footer_right_skip % } % Templates for the footer area. Again the margins are handled in stages: % here we do have a box for the content so the right skip is used, and we % avoid an overfull box by including consideration of the right margin of the % page layout. % \begin{macrocode} \NewTemplateType { footer } { 0 } \DeclareTemplateInterface { footer } { talk } { 0 } { background-color : tokenlist , color : tokenlist , element-order : commalist , font : tokenlist = \tiny , left-hspace : length = \Gm@lmargin , right-hspace : length = \Gm@rmargin , separator : tokenlist = \hfil } \DeclareTemplateCode { footer } { talk } { 0 } { background-color = \l_@@_footer_bg_tl , color = \l_@@_footer_fg_tl , element-order = \l_@@_footer_order_clist , font = \l_@@_footer_font_tl , left-hspace = \l_@@_footer_left_skip , right-hspace = \l_@@_footer_right_skip , separator = \l_@@_footer_sep_tl } { \noindent \@@_wallpaper_hrule:Nnn \l_@@_footer_bg_tl { \footskip } { \Gm@bmargin - \footskip } \skip_horizontal:n { \l_@@_footer_left_skip } \vbox_set_to_wd:Nnn \l_@@_tmp_box { \paperwidth - \l_@@_footer_left_skip - \l_@@_footer_right_skip } { \tl_if_empty:NF \l_@@_footer_fg_tl { \color_select:V \l_@@_footer_fg_tl } \l_@@_footer_font_tl \clist_pop:NNT \l_@@_footer_order_clist \l_@@_tmp_tl { \ExpandArgs { nVv } \UseInstance { footer-element } \l_@@_tmp_tl { @ \@@_metadata_name:n { \l_@@_tmp_tl } } \clist_map_inline:Nn \l_@@_footer_order_clist { \tl_if_empty:cF { @ \@@_metadata_name:n { ##1 } } { \l_@@_footer_sep_tl \ExpandArgs { nnv } \UseInstance { footer-element } {##1} { @ \@@_metadata_name:n { ##1 } } } } } \hfil } \box_use_drop:N \l_@@_tmp_box \skip_horizontal:n { \l_@@_footer_right_skip - \Gm@rmargin } } \DeclareInstance { footer } { std } { talk } { } \AddToHook { begindocument } { \DeclareInstanceCopy { footer } { wallpaper } { std } \EditInstance { footer } { wallpaper } { element-order = } } % \end{macrocode} % \end{variable} % \begin{macro}[EXP]{\@@_metadata_name:n} % A simple auxiliary to shorten metadata names if appropriate. Full expansion % is applied as this avoids any issue with stored names. % \begin{macrocode} \cs_new:Npn \@@_metadata_name:n #1 { \tl_if_exist:cTF { @ short #1 } { short #1 } {#1} } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_wallpaper_hrule:Nnn} % A simple abstraction for the top and bottom rules on the page. % \begin{macrocode} \cs_new_protected:Npn \@@_wallpaper_hrule:Nnn #1#2#3 { \skip_horizontal:n { -\Gm@lmargin } \tl_if_empty:NF #1 { \group_begin: \color_select:V #1 \rule:nnn { \paperwidth } {#2} {#3} \skip_horizontal:n { -\paperwidth } \group_end: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\ps@plain, \ps@wallpaper, \ps@talk} % Install a standard header and footer template, and redefine the % \texttt{plain} one as this will be used for frames without % \enquote{wallpaper} which still need core links, \foreign{etc}. We also % provide a version that only shows the visual elements: this is deliberately % using the same settings as the main templates. % \begin{macrocode} \cs_set_nopar:Npn \ps@plain { \cs_set_nopar:Npn \@oddhead { \hfil } \cs_set_nopar:Npn \@oddfoot { } \cs_set_eq:NN \@evenhead \@oddhead \cs_set_eq:NN \@evenfoot \@oddfoot } \cs_set_nopar:Npn \ps@wallpaper { \cs_set_nopar:Npn \@oddhead { \UseInstance { header } { wallpaper } \hfil } \cs_set_nopar:Npn \@oddfoot { \UseInstance { footer } { wallpaper } \hfil } \cs_set_eq:NN \@evenhead \@oddhead \cs_set_eq:NN \@evenfoot \@oddfoot } \cs_new_nopar:Npn \ps@talk { \cs_set_nopar:Npn \@oddhead { \UseInstance { header } { std } \hfil } \cs_set_nopar:Npn \@oddfoot { \UseInstance { footer } { std } } \cs_set_eq:NN \@evenhead \@oddhead \cs_set_eq:NN \@evenfoot \@oddfoot } \pagestyle { talk } % \end{macrocode} % \end{macro} % % \subsection{The \texttt{frame} environment} % % \begin{variable}{\l_@@_frame_bool} % To track whether we are inside a frame or not. % \begin{macrocode} \bool_new:N \l_@@_frame_bool % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_frame_suppl_int} % Used to track saved supplementary frames. % \begin{macrocode} \int_new:N \g_@@_frame_suppl_int % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_frame_tag_bool} % To track when a frame is being tagged: mainly needed for the header % (and as a result global). % \begin{macrocode} \bool_new:N \g_@@_frame_tag_bool % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_frame_verb_bool} % Indicates that material was collected verbatim (and thus needs rescanning). % \begin{macrocode} \bool_new:N \l_@@_frame_verb_bool % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_frame_int, \c@frame, \theframe, \@framenumber} % The overall frame number, including \LaTeX{} counter-like access. % \begin{macrocode} \int_new:N \g_@@_frame_int \cs_new_eq:NN \c@frame \g_@@_frame_int \cs_new:Npn \theframe { \@arabic \c@frame } \cs_new:Npn \@framenumber { \arabic { frame } } % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_appendixframe_int, \c@appendixframe, \theappendixframe, \@appendixframenumber} % The overall frame number, including \LaTeX{} counter-like access. % \begin{macrocode} \int_new:N \g_@@_appendixframe_int \cs_new_eq:NN \c@appendixframe \g_@@_appendixframe_int \cs_new:Npn \theappendixframe { \@arabic \c@appendixframe } \cs_new:Npn \@appendixframenumber { \arabic { appendixframe } } % \end{macrocode} % \end{variable} % % \begin{variable}{\@frames} % The total frames can be handled using the kernel properties. % \begin{macrocode} \property_new:nnnn { frames } { shipout } { -1 } { \int_eval:n { \g_@@_frame_int - \g_@@_appendixframe_int } } \cs_new:Npn \@frames { \property_ref:nn { lastpage } { frames } } % \end{macrocode} % \end{variable} % % \begin{variable}{\@totalframes} % The total frames can be handled using the kernel properties. % \begin{macrocode} \property_new:nnnn { totalframes } { shipout } { -1 } { \int_use:N \g_@@_frame_int } \cs_new:Npn \@totalframes { \property_ref:nn { lastpage } { totalframes } } % \end{macrocode} % \end{variable} % % \begin{variable}{\@appendixframes} % The total frames can be handled using the kernel properties. % \begin{macrocode} \property_new:nnnn { appendixframes } { shipout } { -1 } { \int_use:N \g_@@_appendixframe_int } \cs_new:Npn \@appendixframes { \property_ref:nn { lastpage } { appendixframes } } % \end{macrocode} % \end{variable} % % \begin{variable}{\l_talk_frame_properties_clist} % Currently somewhat experimental: not used by us, but helpful for users. % \begin{macrocode} \clist_new:N \l_talk_frame_properties_clist % \end{macrocode} % \end{variable} % % \begin{macrocode} \AddToHook { enddocument / afterlastpage } { \property_record:nn { lastpage } { appendixframes , frames , totalframes } } % \end{macrocode} % % \begin{macro}{\@@_latexe_frame:n} % As we will need to re-define \cs{frame} but have it available inside % frames, a copy is made here. % \begin{macrocode} \NewCommandCopy \@@_latexe_frame:n \frame % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_frame_process:nn, \@@_frame_process_aux:nn} % \begin{macro}{\@@_frame_save:nn, \@@_frame_save:en} % Here, the frame content is received as the argument. As frames can be % saved either by the user or automatically that is handled in an % auxiliary. % \begin{macrocode} \cs_new_protected:Npn \@@_frame_process:nn #1#2 { \@@_decode_parse:n {#1} \bool_if:NT \l_@@_decode_mode_bool { \@@_frame_process_aux:nn {#1} {#2} } } \cs_new_protected:Npn \@@_frame_process_aux:nn #1#2 { \int_gincr:N \g_@@_frame_int \bool_if:NT \l_@@_appendix_bool { \int_gincr:N \g_@@_appendixframe_int } \bool_set_true:N \l_@@_frame_bool \str_if_empty:NF \l_@@_frame_name_str { \@@_frame_save:en { \l_@@_frame_name_str } {#2} } \bool_if:NTF \l_@@_frame_suppl_bool { \str_case:Vn \l_@@_suppl_mode_str { { appendix } { \int_gincr:N \g_@@_frame_suppl_int \@@_frame_save:en { appendix . \int_use:N \g_@@_frame_suppl_int } {#2} } { in-place } { \@@_slide:nn {#1} {#2} } } } { \@@_slide:nn {#1} {#2} } } \cs_new_protected:Npn \@@_frame_save:nn #1#2 { \tl_if_exist:cT { g_@@_frame_ #1 _tl } { \msg_error:nnn { talk } { duplicate-frame-name } {#1} } \tl_clear_new:c { g_@@_frame_ #1 _tl } \tl_gset:ce { g_@@_frame_ #1 _tl } { { \bool_if:NTF \l_@@_frame_verb_bool { true } { false } } { \exp_not:n {#2} } } } \cs_generate_variant:Nn \@@_frame_save:nn { e } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{frame, frame*} % The definition for the \env{frame} and \env{frame*} environments: the exact % interface at both the document and code levels is still open. % \begin{macrocode} \bool_if:NTF \l_@@_frame_title_bool { \RenewDocumentEnvironment { frame } { D <> { all } = { action-spec } O { } +m +b } { \str_clear:N \l_@@_frame_name_str \keys_set:nn { talk / frame } {#2} \bool_set_false:N \l_@@_frame_verb_bool \@@_frame_process:nn {#1} { \frametitle {#3} #4 } } { } \NewDocumentEnvironment { frame* } { D <> { all } = { action-spec } O { } +m c } { \str_clear:N \l_@@_frame_name_str \keys_set:nn { talk / frame } {#2} \bool_set_true:N \l_@@_frame_verb_bool \tl_gset:Nn \g_@@_frame_title_tl {#3} \exp_args:Nne \@@_frame_process:nn {#1} { \tl_to_str:n { \frametitle } \exp_not:n { {#3} #4 } } } { } } { \RenewDocumentEnvironment { frame } { !D <> { all } = { action-spec } !O { } +b } { \str_clear:N \l_@@_frame_name_str \keys_set:nn { talk / frame } {#2} \bool_set_false:N \l_@@_frame_verb_bool \@@_frame_process:nn {#1} {#3} } { } \NewDocumentEnvironment { frame* } { !D <> { all } = { action-spec } !O { } c } { \str_clear:N \l_@@_frame_name_str \keys_set:nn { talk / frame } {#2} \bool_set_true:N \l_@@_frame_verb_bool \@@_frame_process:nn {#1} {#3} } { } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_frame_reuse:nn} % \begin{macro}{\@@_frame_reuse_aux:nn} % \begin{macro}{\@@_frame_reuse_aux:nnn} % \begin{macro}{\reuseframe, \againframe} % Reusing a frame is largely a question of passing the correct stored % information along after setting the flag for re-scanning. % \begin{macrocode} \cs_new_protected:Npn \@@_frame_reuse:nn #1#2 { \tl_if_exist:cTF { g_@@_frame_ #2 _tl } { \exp_args:Nv \@@_frame_reuse_aux:nn { g_@@_frame_ #2 _tl } {#1} } { \msg_error:nnn { talk } { unknown-frame-name } {#2} } } \cs_new_protected:Npn \@@_frame_reuse_aux:nn #1#2 { \@@_frame_reuse_aux:nnn #1 {#2} } \cs_new_protected:Npn \@@_frame_reuse_aux:nnn #1#2#3 { \use:c { bool_set_ #1 :N } \l_@@_frame_verb_bool \@@_frame_process:nn {#3} {#2} } \NewDocumentCommand \reuseframe { D <> { all } = { action-spec } O { } m } { \group_begin: \keys_set:nn { talk / frame } {#2} \str_clear:N \l_@@_frame_name_str \@@_frame_reuse:nn {#1} {#3} \group_end: } \NewCommandCopy \againframe \reuseframe % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % Supplementary frames deferred to the appendix need to be swept up. If % necessary, the appendix is triggered. % \begin{macrocode} \AddToHook { enddocument } { \int_compare:nNnT \g_@@_frame_suppl_int > 0 { \bool_if:NF \l_@@_appendix_bool { \appendix } \int_step_inline:nn \g_@@_frame_suppl_int { \@@_frame_reuse:nn { } { appendix . #1 } } } } % \end{macrocode} % % \begin{macrocode} \msg_new:nnnn { talk } { overheight-header } { Overheight~header~on~frame~ \int_use:N \g_@@_frame_int ! } { Typesetting~the~frame~header,~there~was~too~much~vertical~space~used. You~likely~need~to~adjusst~the~overall~space~available~for~frame~titles. } \msg_new:nnnn { talk } { unknown-frame-name } { Unknown~frame~name~"#1"! } { You~asked~to~reuse~the~contents~of~a~frame~with~the~name~"#1",~ but~that~name~was~never~defined. } \msg_new:nnnn { talk } { duplicate-frame-name } { Duplicate~frame~name~"#1"! } { You~asked~to~save~the~contents~of~this~frame~with~the~name~"#1",~ but~that~name~has~already~been~used. } % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex