scrn-but.mkvi / last modification: 2020-01-30 14:16
%D \module
%D   [       file=scrn-but, % moved code
%D        version=1995.01.01,
%D          title=\CONTEXT\ Core Macros,
%D       subtitle=Interaction,
%D         author=Hans Hagen,
%D           date=\currentdate,
%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
%C
%C This module is part of the \CONTEXT\ macro||package and is
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.

\writestatus{loading}{ConTeXt Screen Macros / Buttons}

\registerctxluafile{scrn-but}{}

\unprotect

%D Buttons are just what their names says: things that can be clicked (pushed) on.
%D They are similar to \type{\goto}, except that the text argument is not
%D interpreted. Furthermore one can apply anything to them that can be done with
%D \type {\framed}.
%D
%D \startbuffer
%D \button[width=3cm,height=1.5cm]{Exit}[ExitViewer]
%D \stopbuffer
%D
%D \typebuffer
%D
%D gives
%D
%D \getbuffer
%D
%D This command is formally specified as:
%D
%D \showsetup{button}
%D
%D The characteristics can be set with:
%D
%D \showsetup{setupbuttons}

\installcorenamespace{button}
\installcorenamespace{buttonlocation}

\installframedcommandhandler \??button {button} \??button

\let\setupbuttons\setupbutton

\appendtoks
    \setuevalue\currentbutton{\scrn_button_direct{\currentbutton}}%
\to \everydefinebutton

\unexpanded\def\scrn_button_direct#tag%
  {\begingroup
   \edef\currentbutton{#tag}%
   \doifelselocation
     {\dosingleempty\scrn_button_direct_status}%
     {\dosingleempty\scrn_button_direct_ignore}}

\def\scrn_button_direct_status
  {\doifelse{\buttonparameter\c!state}\v!start
     \scrn_button_direct_indeed
     \scrn_button_direct_ignore}

% empty=yes
%
% \button[settings]{}[action] % normally used at the tex end

\def\scrn_button_direct_indeed[#settings]#text[#action]%
  {\iffirstargument
     \setupcurrentbutton[#settings]%
   \fi
   % no \dontleavehmode as it will force a vbox to \hsize which then
   % spoils a tight box
   \hpack\bgroup
     \scrn_button_make
       \buttonparameter
       \inheritedbuttonframed
       \letbuttonparameter
       \setbuttonparameter
       {#text}%
       {#action}%
   \egroup
   \endgroup}

\def\scrn_button_direct_ignore[#settings]#text[#destination]%
  {\endgroup}

\definebutton[button] % english

\setupbuttons
  [\c!state=\v!start,
   \c!width=\v!fit,
   \c!height=\v!broad,
   \c!offset=0.25\emwidth,
   \c!frame=\v!on,
   \c!background=,
   \c!backgroundcolor=,
   \c!foregroundstyle=\buttonparameter\c!style,
   \c!foregroundcolor=\buttonparameter\c!color,
   \c!style=\interactionparameter\c!style,
   \c!color=\interactionparameter\c!color,
   \c!contrastcolor=\interactionparameter\c!contrastcolor,
   \c!samepage=\v!yes,
   \c!unknownreference=\v!yes,
   \c!distance=\zeropoint] % for menubuttons

%D \macros
%D   {overlaybutton}
%D
%D For converience we provide:
%D
%D \starttyping
%D \overlaybutton[reference]
%D \stoptyping
%D
%D This command can be used to define overlays an/or can be used in the whatevertext
%D areas, like:
%D
%D \starttyping
%D \defineoverlay[PrevPage][\overlaybutton{PrevPage}]
%D \setupbackgrounds[page][background=PrevPage]
%D \setuptexttexts[\overlaybutton{NextPage}]
%D \stoptyping
%D
%D For practical reasons, this macro accepts square brackets as well as braces.

\unexpanded\def\overlaybutton
  {\dosingleempty\scrn_button_overlay}

\def\scrn_button_overlay[#1]%
  {\iffirstargument
     \scrn_button_overlay_indeed{#1}%
   \else
     \expandafter\scrn_button_overlay_indeed
   \fi}

\def\scrn_button_overlay_indeed#1%
  {\iflocation
     \gotobox{\overlayfakebox}[#1]%
   \fi}

%D The renderers:

\expandafter\let\csname\??buttonlocation\v!yes    \endcsname\zerocount
\expandafter\let\csname\??buttonlocation\v!empty  \endcsname\plusone
\expandafter\let\csname\??buttonlocation\v!no     \endcsname\plustwo
\expandafter\let\csname\??buttonlocation\v!none   \endcsname\plusthree
\expandafter\let\csname\??buttonlocation\v!normal \endcsname\plusone % default
\expandafter\let\csname\??buttonlocation\s!default\endcsname\plusone % default
\expandafter\let\csname\??buttonlocation          \endcsname\plusone % default

\newconditional\c_scrn_button_skipped

\def\scrn_button_make#currentparameter#inheritedframed#letparameter#setparameter#text#action%
  {\begingroup
   \attribute\referenceattribute\attributeunsetvalue
   \global\setfalse\c_scrn_button_skipped
   \chardef\locationboxpagestate\csname\??buttonlocation#currentparameter\c!samepage\endcsname % ?? bt: todo
   \doifelsereferencefound{#action}\scrn_button_make_yes\scrn_button_make_nop
     #currentparameter%
     #inheritedframed%
     #letparameter%
     #setparameter
     {#text}%
   \endgroup}

\def\scrn_button_make_yes
  {\scratchcounter\referencerealpage\relax % called in otr
   \ifcase\scratchcounter
     \expandafter\scrn_button_make_normal   % no state :  something else than a page reference
   \else\ifnum\scratchcounter=\realpageno
     \expandafter\expandafter\expandafter\scrn_button_make_yes_same
   \else
     \expandafter\expandafter\expandafter\scrn_button_make_yes_other
   \fi\fi}

\def\scrn_button_make_yes_same
  {\ifcase\locationboxpagestate\relax
     \expandafter\scrn_button_make_contrast % same page:  yes: same page or not ... todo
   \or
     \expandafter\scrn_button_make_empty    % same page:  empty but frame: no click
   \or
     \expandafter\scrn_button_make_nothing  % same page:  empty no frame: no
   \else
     \expandafter\scrn_button_make_skipped  % same page:  nothing at all
   \fi}

\def\scrn_button_make_yes_other
  {\ifcase\locationboxpagestate\relax
     \expandafter\scrn_button_make_normal   % other page: yes: same page or not ... todo
   \or
     \expandafter\scrn_button_make_normal   % other page: empty but frame: no click
   \or
     \expandafter\scrn_button_make_normal   % other page: empty no frame: no
   \else
     \expandafter\scrn_button_make_skipped  % other page: nothing at all
   \fi}

\def\scrn_button_make_nop
  {\ifcase\locationboxpagestate\relax
      \expandafter\scrn_button_make_framed
   \or
      \expandafter\scrn_button_make_empty
   \or
      \expandafter\scrn_button_make_nothing
   \or
      \expandafter\scrn_button_make_skipped
   \fi}

\def\scrn_button_make_framed#currentparameter#inheritedframed#letparameter#setparameter#text%
  {#inheritedframed{\ignorespaces#text\removeunwantedspaces}}

\def\scrn_button_make_skipped#currentparameter#inheritedframed#letparameter#setparameter#text%
  {\global\settrue\c_scrn_button_skipped}

\def\scrn_button_make_normal#currentparameter#inheritedframed#letparameter#setparameter#text%
  {\clf_injectcurrentreference
   \hbox attr \referenceattribute \lastreferenceattribute
     {#inheritedframed{\ignorespaces#text\removeunwantedspaces}}}

\def\scrn_button_make_contrast#currentparameter#inheritedframed#letparameter#setparameter#text%
  {\clf_injectcurrentreference
   \hbox attr \referenceattribute \lastreferenceattribute
     {#setparameter\c!foregroundcolor{#currentparameter\c!contrastcolor}%
      #inheritedframed{\ignorespaces#text\removeunwantedspaces}}}

\def\scrn_button_make_empty#currentparameter#inheritedframed#letparameter#setparameter#text%
  {#letparameter\c!empty\v!yes
   #inheritedframed{\ignorespaces#text\removeunwantedspaces}}

\def\scrn_button_make_nothing#currentparameter#inheritedframed#letparameter#setparameter#text%
  {#letparameter\c!empty\v!yes
   #letparameter\c!frame\empty
   #letparameter\c!background\empty
   #inheritedframed{\ignorespaces#text\removeunwantedspaces}}

%D Menus:
%D
%D \starttyping
%D \setuppapersize
%D   [S6][S6]
%D
%D \setuplayout
%D   [backspace=6cm,      cutspace=6cm,
%D    leftedge=3cm,       rightedge=3cm,
%D    leftmargin=1cm,     rightmargin=1cm,
%D    margindistance=5mm, edgedistance=5mm,
%D    topspace=4cm,       bottomspace=4cm,
%D    header=0pt,         footer=0pt,
%D    top=1cm,            bottom=1cm,
%D    topdistance=5mm,    bottomdistance=5mm,
%D    width=middle,       height=middle]
%D
%D \setupinteraction
%D   [state=start,
%D    menu=on]
%D
%D \setupinteractionmenu
%D   [right]
%D   [state=start,background=color,frame=off,backgroundcolor=red,color=white,contrastcolor=blue]
%D \setupinteractionmenu
%D   [left]
%D   [state=start,background=color,frame=off,backgroundcolor=green,color=white]
%D \setupinteractionmenu
%D   [top]
%D   [state=start,background=color,frame=off,backgroundcolor=blue,color=white]
%D \setupinteractionmenu
%D   [bottom]
%D   [state=start,background=color,frame=off,backgroundcolor=yellow,color=white]
%D
%D \setupinteractionmenu
%D   [left]
%D   [state=local]
%D \setupinteractionmenu
%D   [bottom]
%D   [state=local]
%D
%D \startinteractionmenu[right]
%D     \startbut [page(2)] Page 2 \stopbut
%D     \startbut [page(1)] Page 1 \stopbut
%D     \includemenu[left]
%D     \includemenu[bottom]
%D \stopinteractionmenu
%D
%D \startinteractionmenu[left]
%D     \startbut [page(1)] Page 1 \stopbut
%D     \startbut [page(2)] Page 2 \stopbut
%D \stopinteractionmenu
%D
%D \startinteractionmenu[top]
%D     \startbut [page(1)] Page 1 \stopbut
%D     \startbut [page(2)] Page 2 \stopbut
%D \stopinteractionmenu
%D
%D \startinteractionmenu[bottom]
%D     \startbut [page(2)] Page 2 \stopbut
%D     \startbut [page(1)] Page 1 \stopbut
%D \stopinteractionmenu
%D \stoptyping
%D
%D \starttyping
%D \startinteractionmenu[rechts]
%D   \startbut [eerste]  eerste  \stopbut
%D   \starttxt hello world       \stoptxt
%D   \startbut [tweede]  tweede  \stopbut
%D   \startnop                   \stopnop
%D   \startbut [tweede]  tweede  \stopbut
%D   \startrul whow              \stoprul
%D   \startbut [tweede]  tweede  \stopbut
%D   \startraw hello world       \stopraw
%D   \startbut [tweede]  tweede  \stopbut
%D   \startcom \vfill            \stopcom
%D   \startbut [derde]   derde   \stopbut
%D \stopinteractionmenu
%D \stoptyping
%D
%D \starttyping
%D \setupinteractionmenu[right][samepage=yes,  unknownreference=yes]
%D \setupinteractionmenu[right][samepage=empty,unknownreference=empty]
%D \setupinteractionmenu[right][samepage=no,   unknownreference=no]
%D \setupinteractionmenu[right][samepage=none, unknownreference=none]
%D \stoptyping

\installcorenamespace{menu}
\installcorenamespace{menutoks}
\installcorenamespace{menustate}
\installcorenamespace{menupacker}
\installcorenamespace{menualign}

\installframedcommandhandler \??menu {interactionmenu} \??menu

\let\setupinteractionmenus\setupinteractionmenu

\let\scrn_menu_action\relax

\let\scrn_menu_define_original\defineinteractionmenu

\unexpanded\def\defineinteractionmenu
  {\dotripleempty\scrn_menu_define}

\def\scrn_menu_define[#tag][#category][#settings]% category reflects location, settings can be parent
  {\ifthirdargument
     \doifelseassignment{#settings}%
       {\scrn_menu_define_original[#tag][#category][\c!category=#category,#settings]}% child definition
       {\scrn_menu_define_original[#tag][#settings][\c!category=#category]}%         % child definition
     \scrn_menu_register{#tag}{#category}%
   \else\ifsecondargument
     \doifelseassignment{#category}%
       {\scrn_menu_define_original[#tag][#category]}%                                % root definition
       {\scrn_menu_define_original[#tag][#category][\c!category=#category]%          % child definition
        \scrn_menu_register{#tag}{#category}}%
  \else
     \scrn_menu_define_original[#tag]%                                               % root definition
   \fi\fi}

\def\scrn_menu_register#tag#category%
  {\ifcsname\??menutoks#category\endcsname \else
     \expandafter\newtoks \csname\??menutoks#category\endcsname
     \expandafter\setfalse\csname\??menustate#category\endcsname
   \fi
   \normalexpanded{\csname\??menutoks#category\endcsname{\the\csname\??menutoks#category\endcsname\scrn_menu_action{#tag}}}}

\def\scrn_menu_actions#category%
  {\the\csname\??menutoks#category\endcsname}

%D Fill menus:

\normalexpanded{\def\expandafter\noexpand\csname\e!start\v!interactionmenu\endcsname[#tag]#content\expandafter\noexpand\csname\e!stop\v!interactionmenu\endcsname}%
  {\def\currentinteractionmenu{#tag}%
   \expandafter\settrue\csname\??menustate\interactionmenuparameter\c!category\endcsname
   \setinteractionmenuparameter\c!menu{#content}}

\def\resetinteractionmenu[#tag]%
  {\def\currentinteractionmenu{#tag}%
   \resetinteractionmenuparameter\c!menu}

%D Placement of menus:
%D
%D The offset mechanism is not the same as in in \MKII. There we
%D adapted automatically to offsets in the text backgrounds. Here we
%D have a bit more (but manual) control.
%D
%D \starttyping
%D \setupbackgrounds
%D   [text][text]
%D   [background=color,backgroundcolor=gray,backgroundoffset=2mm]
%D
%D \setupbackgrounds
%D   [text]
%D   [rightedge,leftedge]
%D   [background=color,backgroundcolor=gray]
%D
%D \setupbackgrounds
%D   [top,bottom]
%D   [text]
%D   [background=color,backgroundcolor=gray]
%D
%D \setupinteractionmenu
%D   [right]
%D   [topoffset=0mm,bottomoffset=0mm]
%D
%D \setupinteractionmenu
%D   [top]
%D   [topoffset=2mm,bottomoffset=2mm,rightoffset=2mm,leftoffset=2mm]
%D \stoptyping
%D
%D The no longer hard coded text areas offset compensation makes tuning
%D easier. After all, menus need some setup anyway. The offsets are
%D added to the width or height (this is different from \MKII).

\newbox  \b_scrn_menu

\newdimen\d_scrn_menu_next_distance
\newdimen\d_scrn_menu_final_width
\newdimen\d_scrn_menu_final_height
\newdimen\d_scrn_menu_used_width
\newdimen\d_scrn_menu_used_height
\newdimen\d_scrn_menu_asked_width
\newdimen\d_scrn_menu_asked_height
\newdimen\d_scrn_menu_offset_top
\newdimen\d_scrn_menu_offset_bottom
\newdimen\d_scrn_menu_offset_left
\newdimen\d_scrn_menu_offset_right

\newconditional\c_scrn_menu_zerodimensions

\def\scrn_menu_set_used
  {\doassigncheckedframeoffset\d_scrn_menu_offset_left  {\interactionmenuparameter\c!leftoffset  }%
   \doassigncheckedframeoffset\d_scrn_menu_offset_right {\interactionmenuparameter\c!rightoffset }%
   \doassigncheckedframeoffset\d_scrn_menu_offset_top   {\interactionmenuparameter\c!topoffset   }%
   \doassigncheckedframeoffset\d_scrn_menu_offset_bottom{\interactionmenuparameter\c!bottomoffset}%
   \d_scrn_menu_asked_width  \interactionmenuparameter\c!maxwidth
   \d_scrn_menu_asked_height \interactionmenuparameter\c!maxheight
   \d_scrn_menu_used_width\dimexpr
     \d_scrn_menu_asked_width  + \d_scrn_menu_offset_left + \d_scrn_menu_offset_right
   \relax
   \d_scrn_menu_used_height\dimexpr
     \d_scrn_menu_asked_height + \d_scrn_menu_offset_top  + \d_scrn_menu_offset_bottom
   \relax}

\def\scrn_menu_set_final
  {\d_scrn_menu_final_width \namedinteractionmenuparameter\askedinteractionmenulocation\c!maxwidth
   \d_scrn_menu_final_height\namedinteractionmenuparameter\askedinteractionmenulocation\c!maxheight}

\def\scrn_menu_apply_final
  {\ifconditional\c_scrn_menu_zerodimensions
     \wd\b_scrn_menu\zeropoint
     \ht\b_scrn_menu\zeropoint
   \else
     \wd\b_scrn_menu\d_scrn_menu_final_width
     \ht\b_scrn_menu\d_scrn_menu_final_height
   \fi
   \dp\b_scrn_menu\zeropoint}

\def\scrn_menu_apply_used
  {\ifzeropt\d_scrn_menu_offset_left \else
     \setbox\b_scrn_menu\hbox{\hskip-\d_scrn_menu_offset_left   \box\b_scrn_menu}%
   \fi
   \ifzeropt\d_scrn_menu_offset_bottom \else
     \setbox\b_scrn_menu\hbox{\lower \d_scrn_menu_offset_bottom \box\b_scrn_menu}%
   \fi
   \wd\b_scrn_menu\d_scrn_menu_asked_width
   \ht\b_scrn_menu\d_scrn_menu_asked_height
   \dp\b_scrn_menu\zeropoint}

\setvalue{\??menualign\v!right     }{\let\scrn_menu_left_align\raggedright}
\setvalue{\??menualign\v!left      }{\let\scrn_menu_left_align\raggedleft}
\setvalue{\??menualign\v!flushright}{\let\scrn_menu_left_align\raggedleft}
\setvalue{\??menualign\v!flushleft }{\let\scrn_menu_left_align\raggedright}
\setvalue{\??menualign\v!middle    }{\let\scrn_menu_left_align\raggedcenter}
\setvalue{\??menualign\v!low       }{\let\scrn_menu_top_align\vss\let\scrn_menu_bottom_align\relax}
\setvalue{\??menualign\v!high      }{\let\scrn_menu_top_align\relax\let\scrn_menu_bottom_align\vss}
\setvalue{\??menualign\v!lohi      }{\let\scrn_menu_top_align\vss\let\scrn_menu_bottom_align\vss}

\let\scrn_menu_left_align  \relax
\let\scrn_menu_right_align \relax
\let\scrn_menu_top_align   \relax
\let\scrn_menu_bottom_align\relax

\def\scrn_menu_set_align
  {\csname\??menualign\interactionmenuparameter\c!itemalign\endcsname}

%D Hook into the pagebuilder (as less testing as possible):

\def\scrn_menu_insert
  {\iflocation
     \expandafter\scrn_menu_insert_checked
   \else
     \expandafter\gobbleoneargument
   \fi}

\def\scrn_menu_insert_checked#location%
  {\ifconditional\csname\??menustate#location\endcsname
     \scrn_menu_insert_indeed{#location}%
   \fi}

\def\scrn_menu_insert_indeed#location%
  {\begingroup
   \xdef\askedinteractionmenulocation{#location}%
   \scrn_menu_set_final
   \ifcase\d_scrn_menu_final_width \else \ifcase\d_scrn_menu_final_height \else
     \forgetall
     \global\d_scrn_menu_next_distance\zeropoint
     \let\scrn_menu_action\scrn_menu_package_indeed
     \the\everysetmenucommands
     \csname\??menupacker\namedinteractionmenuparameter\askedinteractionmenulocation\c!alternative\endcsname
   \fi \fi
   \endgroup}

%D This calls: % can be \c!command for vertical/horizontal

\setvalue{\??menupacker\v!vertical}% all menus
  {\let\scrn_menu_packager\scrn_menu_packager_vertical
   \setbox\b_scrn_menu\hbox{\scrn_menu_actions\askedinteractionmenulocation}%
   \scrn_menu_apply_final
   \box\b_scrn_menu}

\setvalue{\??menupacker\v!horizontal}% all menus
  {\let\scrn_menu_packager\scrn_menu_packager_horizontal
   \setbox\b_scrn_menu\vbox{\scrn_menu_actions\askedinteractionmenulocation}%
   \scrn_menu_apply_final
   \box\b_scrn_menu}

% stop : skipped
% start: processed
% local: skipped but can be included
% empty: processed but invisible

\unexpanded\def\scrn_menu_package_indeed#tag% one menu
  {\begingroup
   \edef\currentinteractionmenu{#tag}%
   \edef\currentinteractionmenustate{\interactionmenuparameter\c!state}%
   \ifx\currentinteractionmenustate\v!start
     \scrn_menu_packager
   \else\ifx\currentinteractionmenustate\v!empty
     \scrn_menu_packager
   \fi\fi
   \endgroup}

%D With the packager being one of:

\let\currentinteractionmenudistance\!!zeropoint

\def\scrn_menu_packager_vertical
  {\scrn_menu_set_used
   \hskip\d_scrn_menu_next_distance
   \setbox\b_scrn_menu\hbox to \d_scrn_menu_used_width
     {\ifx\currentinteractionmenustate\v!empty \else
        \interactionmenuparameter\c!left
        \scrn_menu_package_vertical{\directinteractionmenuparameter\c!menu}%
        \interactionmenuparameter\c!right
      \fi}%
   \edef\currentinteractionmenudistance{\interactionmenuparameter\c!distance}%
   \ifx\currentinteractionmenudistance\v!overlay
     \global\d_scrn_menu_next_distance\zeropoint
     \wd\b_scrn_menu\zeropoint
   \else
     \global\d_scrn_menu_next_distance\currentinteractionmenudistance
     \scrn_menu_apply_used
   \fi
   \box\b_scrn_menu}

\def\scrn_menu_packager_horizontal
  {\scrn_menu_set_used
   \vskip\d_scrn_menu_next_distance
   \scrn_menu_set_align
   \setbox\b_scrn_menu\vbox to \d_scrn_menu_used_height
     {\ifx\currentinteractionmenustate\v!none \else
        \scrn_menu_top_align
        \interactionmenuparameter\c!before
        \scrn_menu_package_horizontal{\directinteractionmenuparameter\c!menu}%
        \interactionmenuparameter\c!after
        \scrn_menu_bottom_align
      \fi}%
   \edef\currentinteractionmenudistance{\interactionmenuparameter\c!distance}%
   \ifx\currentinteractionmenudistance\v!overlay
     \global\d_scrn_menu_next_distance\zeropoint
     \offinterlineskip
     \dp\b_scrn_menu\zeropoint
     \ht\b_scrn_menu\zeropoint
   \else
     \global\d_scrn_menu_next_distance\currentinteractionmenudistance
     \scrn_menu_apply_used
   \fi
   \box\b_scrn_menu}

%D For a right menu, a sequence of calls to \type
%D {right_menu_button} is generated.
%D
%D \starttyping
%D right_menu_button (n, p, s=0/1/2, x, y, w, h, d) ;
%D \stoptyping
%D
%D Here, n is the number of the button, s a status variable,
%D while the rest is positional info. The status variable is
%D 0, 1 or~2: not found, found and found but current page.

\newcount      \c_scrn_menu_position
\newconstant   \c_scrn_menu_page_mode % 0=notfound  1=found  2=currentpage
\newconditional\c_scrn_menu_positioning
\newtoks       \t_scrn_menu_meta_data

\def\scrn_menu_button_meta_template
  {\askedinteractionmenulocation _menu_button(%
     \number\c_scrn_menu_position,%
     \number\c_scrn_menu_page_mode,%
     \MPpos{\askedinteractionmenulocation:\number\c_scrn_menu_position}%
   );}

\def\MPmenubuttons#1{\the\t_scrn_menu_meta_data}

\appendtoks
    \global\t_scrn_menu_meta_data\emptytoks
\to \everyshipout

\def\scrn_menu_whole_position % cannot happen in previous due to align
  {\setbox\b_scrn_menu\hbox \bgroup
     \hpos{menu:\askedinteractionmenulocation:\the\realpageno}{\box\b_scrn_menu}%
   \egroup}

% removed: \restorestandardblank (vspacing) ... should happen elsewhere

\def\scrn_menu_package_vertical#content%
  {\begingroup
   \global\c_scrn_menu_position\zerocount
   \def\scrn_menu_between_action_indeed{\interactionmenuparameter\c!inbetween}%
   \doifelse{\interactionmenuparameter\c!position}\v!yes\settrue\setfalse\c_scrn_menu_positioning
   \scrn_menu_set_align
   \setbox\b_scrn_menu\vbox to \d_scrn_menu_used_height \bgroup
     \hsize\d_scrn_menu_used_width
     \scrn_menu_left_align
     \interactionmenuparameter\c!before\relax
     \ignorespaces#content\unskip
     \interactionmenuparameter\c!after
     \scrn_menu_right_align
   \egroup
   \ifconditional\c_scrn_menu_positioning
     \scrn_menu_whole_position
   \fi
   \box\b_scrn_menu
   \endgroup}

\def\scrn_menu_package_horizontal#content%
  {\begingroup
   \global\c_scrn_menu_position\zerocount
   \def\scrn_menu_between_action_indeed{\interactionmenuparameter\c!middle}%
   \doifelse{\interactionmenuparameter\c!position}\v!yes\settrue\setfalse\c_scrn_menu_positioning
   \setbox\b_scrn_menu\hbox to \d_scrn_menu_used_width \bgroup
     \interactionmenuparameter\c!left\relax
     \ignorespaces#content\unskip
     \interactionmenuparameter\c!right
   \egroup
   \ifconditional\c_scrn_menu_positioning
     \scrn_menu_whole_position
   \fi
   \box\b_scrn_menu
   \endgroup}

\def\scrn_menu_action_start
  {\dontleavehmode
   \begingroup}

\def\scrn_menu_action_stop
  {\ifconditional\c_scrn_button_skipped \else
     \scrn_menu_between_action_indeed
   \fi
   \endgroup
   \ignorespaces}

\unexpanded\def\scrn_menu_raw_start[#action]#text\stopraw
  {\scrn_menu_action_start
   \gotobox{\ignorespaces#text\unskip}[#action]%
   \scrn_menu_action_stop}

\unexpanded\def\scrn_menu_but_start[#action]#text\stopbut
  {\scrn_menu_action_start
   \ifconditional\c_scrn_menu_positioning
     \expandafter\scrn_button_make_position
   \else
     \expandafter\scrn_button_make
   \fi
     \interactionmenuparameter
     \inheritedinteractionmenuframed
     \letinteractionmenuparameter
     \setinteractionmenuparameter
     {#text}%
     {#action}%
   \scrn_menu_action_stop}

\def\scrn_button_make_position#currentparameter#inheritedframed#letparameter#setparameter#text#action%
  {\global\advance\c_scrn_menu_position\plusone
   \doifelsereferencefound{#action}% 0=not found, 1=same page, >1=elsewhere
     {\c_scrn_menu_page_mode\ifnum\currentreferencerealpage=\realpageno\plusone\else\plustwo\fi}%
     {\c_scrn_menu_page_mode\plustwo}%
   \doglobal\appendetoks
     \scrn_menu_button_meta_template
   \to \t_scrn_menu_meta_data
   \hpos
     {\askedinteractionmenulocation:\number\c_scrn_menu_position}%
     {\scrn_button_make
        #currentparameter%
        #inheritedframed%
        #letparameter%
        #setparameter%
        {#text}%
        {#action}}}

\unexpanded\def\scrn_menu_got_start[#action]#text\stopgot
  {\scrn_menu_action_start
   \letinteractionmenuparameter\c!frame\v!off
   \letinteractionmenuparameter\c!background\empty
   \scrn_button_make
     \interactionmenuparameter
     \inheritedinteractionmenuframed
     \letinteractionmenuparameter
     \setinteractionmenuparameter
     {#text}%
     {#action}%
   \scrn_menu_action_stop}

\unexpanded\def\scrn_menu_nop_start#text\stopnop
  {\scrn_menu_action_start
   \letinteractionmenuparameter\c!frame\v!off
   \letinteractionmenuparameter\c!background\empty
   \letinteractionmenuparameter\c!empty\v!yes
   \inheritedinteractionmenuframed{\ignorespaces#text\removeunwantedspaces}%
   \scrn_menu_action_stop}

\unexpanded\def\scrn_menu_txt_start#text\stoptxt
  {\scrn_menu_action_start
   \letinteractionmenuparameter\c!frame\v!off
   \letinteractionmenuparameter\c!background\empty
   \inheritedinteractionmenuframed{\ignorespaces#text\removeunwantedspaces}%
   \scrn_menu_action_stop}

\unexpanded\def\scrn_menu_rul_start#text\stoprul
  {\scrn_menu_action_start
   \inheritedinteractionmenuframed{\ignorespaces#text\removeunwantedspaces}%
   \scrn_menu_action_stop}

\unexpanded\def\scrn_menu_com_start#text\stopcom
  {\ignorespaces#text\removeunwantedspaces
   \ignorespaces}

\unexpanded\def\scrn_menu_raw#content\\{\scrn_menu_raw_start#content\stopraw} \let\stopraw\relax
\unexpanded\def\scrn_menu_but#content\\{\scrn_menu_but_start#content\stopbut} \let\stopbut\relax
\unexpanded\def\scrn_menu_got#content\\{\scrn_menu_got_start#content\stopgot} \let\stopgot\relax
\unexpanded\def\scrn_menu_nop#content\\{\scrn_menu_nop_start#content\stopnop} \let\stopnop\relax
\unexpanded\def\scrn_menu_txt#content\\{\scrn_menu_txt_start#content\stoptxt} \let\stoptxt\relax
\unexpanded\def\scrn_menu_rul#content\\{\scrn_menu_rul_start#content\stoprul} \let\stoprul\relax
\unexpanded\def\scrn_menu_com#content\\{\scrn_menu_com_start#content\stopcom} \let\stopcom\relax

\newtoks\everysetmenucommands % public

\appendtoks
    \let\raw\scrn_menu_raw \let\startraw\scrn_menu_raw_start \let\stopraw\relax
    \let\but\scrn_menu_but \let\startbut\scrn_menu_but_start \let\stopbut\relax
    \let\got\scrn_menu_got \let\startgot\scrn_menu_got_start \let\stopgot\relax
    \let\nop\scrn_menu_nop \let\startnop\scrn_menu_nop_start \let\stopnop\relax
    \let\txt\scrn_menu_txt \let\starttxt\scrn_menu_txt_start \let\stoptxt\relax
    \let\rul\scrn_menu_rul \let\startrul\scrn_menu_rul_start \let\stoprul\relax
    \let\com\scrn_menu_com \let\startcom\scrn_menu_com_start \let\stopcom\relax
\to \everysetmenucommands

%D Sometimes handy:
%D
%D \starttyping
%D \setupinteractionmenu
%D   [left]
%D   [state=local]
%D
%D \startinteractionmenu[right]
%D     ...
%D     \includemenu[left]
%D     ...
%D \stopinteractionmenu
%D \stoptyping

\unexpanded\def\includemenu[#tag]%
  {\begingroup
   \edef\currentinteractionmenu{#tag}%
   \doif{\interactionmenuparameter\c!state}\v!local
     {\letinteractionmenuparameter\c!state\v!start
      \directinteractionmenuparameter\c!menu}%
   \endgroup}

%D Direct call (todo):

\unexpanded\def\interactionmenu
  {\dodoubleempty\scrn_menu_interaction_menu}

\def\scrn_menu_interaction_menu[#tag][#settings]%
  {\begingroup
   \edef\currentinteractionmenu{#tag}%
   \setupcurrentinteractionmenu[#settings]%
   \scrn_menu_insert{#tag}%
   \endgroup}

%D Plugin handler:

\unexpanded\def\scrn_menu_insert_content_indeed
  {\iflocation % here as we can have a fast turn-off
     \expandafter\firstofoneargument
   \else
     \expandafter\gobbleoneargument
   \fi}

\let\scrn_menu_insert_content_ignore\gobbleoneargument

\appendtoks
    \doifelse{\interactionparameter\c!menu}\v!on
      {\let\scrn_menu_insert_content\scrn_menu_insert_content_indeed}%
      {\let\scrn_menu_insert_content\scrn_menu_insert_content_ignore}%
\to \everysetupinteraction

%D Plugs into the page builder:

\unexpanded\def\scrn_menu_leftedge
  {\hbox to \leftedgewidth \bgroup
     \hsize\leftedgewidth
     \settrue\c_scrn_menu_zerodimensions
     \scrn_menu_insert\v!left
   \egroup
   \hskip-\leftedgewidth}

\unexpanded\def\scrn_menu_rightedge
  {\hbox to \rightedgewidth \bgroup
     \hsize\rightedgewidth
     \settrue\c_scrn_menu_zerodimensions
     \scrn_menu_insert\v!right
   \egroup
   \hskip-\rightedgewidth}

\unexpanded\def\scrn_menu_top
  {\vbox to \topheight \bgroup
     \vsize\topheight
     \settrue\c_scrn_menu_zerodimensions
     \scrn_menu_insert\v!top
     \kern\zeropoint
   \egroup
   \vskip-\topheight}

\unexpanded\def\scrn_menu_bottom
  {\vbox to \bottomheight \bgroup
     \vsize\bottomheight
     \settrue\c_scrn_menu_zerodimensions
     \scrn_menu_insert\v!bottom
     \kern\zeropoint
   \egroup
   \vskip-\bottomheight}

\appendtoks \scrn_menu_insert_content\scrn_menu_leftedge  \to \leftedgetextcontent
\appendtoks \scrn_menu_insert_content\scrn_menu_rightedge \to \rightedgetextcontent
\appendtoks \scrn_menu_insert_content\scrn_menu_top       \to \toptextcontent
\appendtoks \scrn_menu_insert_content\scrn_menu_bottom    \to \bottomtextcontent

%D Initialization (root definitions, main builders):

\defineinteractionmenu [\v!vertical]   [\c!alternative=\v!vertical]
\defineinteractionmenu [\v!horizontal] [\c!alternative=\v!horizontal]

%D Initialization (parent definitions, 4 area builders):

\defineinteractionmenu [\v!right ] [\v!right ] [\v!vertical  ]
\defineinteractionmenu [\v!left  ] [\v!left  ] [\v!vertical  ]
\defineinteractionmenu [\v!top   ] [\v!top   ] [\v!horizontal]
\defineinteractionmenu [\v!bottom] [\v!bottom] [\v!horizontal]

\setupinteraction
  [\c!menu=\v!off]

\setupinteractionmenu
  [\c!offset=.25em,
   \c!position=\v!no,
   \c!frame=\v!on,
   \c!maxwidth=\hsize,
   \c!maxheight=\vsize,
   \c!background=,
   \c!backgroundcolor=,
   \c!foregroundstyle=\interactionmenuparameter\c!style,
   \c!foregroundcolor=\interactionmenuparameter\c!color,
   \c!style=\interactionparameter\c!style,
   \c!color=\interactionparameter\c!color,
   \c!contrastcolor=\interactionparameter\c!contrastcolor,
   \c!state=\v!start,
   \c!samepage=\v!yes,
   \c!unknownreference=\v!empty,
   \c!distance=\bodyfontsize,
   \c!topoffset=\zeropoint,
   \c!bottomoffset=\zeropoint,
   \c!leftoffset=\zeropoint,
   \c!rightoffset=\zeropoint]

\setupinteractionmenu
  [\v!vertical] % not really a menu
  [\c!inbetween=\blank,
   \c!before=,
   \c!after=\vfil,
  %\c!width=\v!fit,
   \c!height=\v!broad]

\setupinteractionmenu
  [\v!horizontal] % not really a menu
  [\c!middle=\hfil,
  %\c!left=\hss,
  %\c!right=\hss,
   \c!width=\v!fit,
   \c!height=\v!broad]

\setupinteractionmenu
  [\v!left]
  [\c!itemalign=\v!flushright,
   \c!maxwidth=\leftedgewidth,
   \c!maxheight=\makeupheight]

\setupinteractionmenu
  [\v!right]
  [\c!itemalign=\v!flushleft,
   \c!maxwidth=\rightedgewidth,
   \c!maxheight=\makeupheight]

\setupinteractionmenu
  [\v!top]
  [\c!itemalign=\v!high,
   \c!maxwidth=\makeupwidth,
   \c!maxheight=\topheight]

\setupinteractionmenu
  [\v!bottom]
  [\c!itemalign=\v!low,
   \c!maxwidth=\makeupwidth,
   \c!maxheight=\bottomheight]

%D Lists:

\definelistalternative [\v!left  ] [\c!renderingsetup=strc:lists:rendering:menu]
\definelistalternative [\v!right ] [\c!renderingsetup=strc:lists:rendering:menu]
\definelistalternative [\v!top   ] [\c!renderingsetup=strc:lists:rendering:menu]
\definelistalternative [\v!bottom] [\c!renderingsetup=strc:lists:rendering:menu]

\startsetups[strc:lists:rendering:menu]
    \startbut[internal(\currentlistentrylocation)]
        \limitatetext\currentlistentrytitle{\listparameter\c!maxwidth}\unknown
    \stopbut
\stopsetups

%D Sometimes handy:

\unexpanded\def\menubutton % tag settings text action
  {\dodoubleempty\scrn_menu_menu_button}

\def\scrn_button_direct_status
  {\doifelse{\buttonparameter\c!state}\v!start
     {\dosingleempty\scrn_button_direct_indeed}%
     {\dosingleempty\scrn_button_direct_ignore}}

\def\scrn_menu_menu_button
  {\iflocation
     \expandafter\scrn_menu_menu_button_indeed
   \else
     \expandafter\scrn_menu_menu_button_ignore
   \fi}

\def\scrn_menu_menu_button_indeed[#menutag][#settings]#text[#action]%
  {\ifsecondargument
     \scrn_menu_menu_button_a
       {#menutag}{#settings}{#text}{#action}%
   \else
     \doifelseassignment{#menutag}\scrn_menu_menu_button_b\scrn_menu_menu_button_c
       {#menutag}{#text}{#action}%
   \fi}

\def\scrn_menu_menu_button_ignore[#menutag][#settings]#text[#action]%
  {}

\def\scrn_menu_menu_button_a#tag#settings#text#action%
  {\doif{\interactionmenuparameter\c!state}\v!start
     {\dontleavehmode \begingroup
        \edef\currentinteractionmenu{#tag}%
        \setupcurrentinteractionmenu[#settings]%
        \scrn_button_make
          \interactionmenuparameter
          \inheritedinteractionmenuframed
          \letinteractionmenuparameter
          \setinteractionmenuparameter
          {#text}%
          {#action}%
      \endgroup}}

\def\scrn_menu_menu_button_b#settings#text#action%
  {\doif{\buttonparameter\c!state}\v!start
     {\dontleavehmode \begingroup
        \let\currentbutton\empty
        \setupcurrentbutton[#settings]%
        \scrn_button_make
          \buttonparameter
          \inheritedbuttonframed
          \letbuttonparameter
          \setbuttonparameter
          {#text}%
          {#action}%
      \endgroup}}

\def\scrn_menu_menu_button_c#tag#text#action%
  {\doif{\interactionmenuparameter\c!state}\v!start
     {\dontleavehmode \begingroup
        \edef\currentinteractionmenu{#tag}%
        \scrn_button_make
          \interactionmenuparameter
          \inheritedinteractionmenuframed
          \letinteractionmenuparameter
          \setinteractionmenuparameter
          {#text}%
          {#action}%
      \endgroup}}

%D Untested:

\unexpanded\def\registermenubuttons
  {\dodoubleempty\scrn_menu_register_menu_buttons}

\def\scrn_menu_register_menu_buttons[#menu][#register]%
  {\begingroup
   \ifsecondargument
     \clf_registerbuttons{#menu}{#register}{\currentlanguage}%
   \else
     \clf_registerbuttons{}{#menu}{\currentlanguage}%
   \fi
   \removeunwantedspaces
   \endgroup}

\def\doregistermenubutton#1#2#3% used at lua end
  {\scrn_menu_menu_button_c{#1}{#2}{#3}%
   \space}

% or less readable:
%
% \def\scrn_menu_register_menu_buttons[#menu][#register]%
%   {\clf_registerbuttons\ifsecondargument{#menu}{#register}\else{}{#menu}\fi{\currentlanguage}}

\protect \endinput