typo-ada.lmt /size: 9084 b    last modification: 2025-02-21 11:03
1if not modules then modules = { } end modules ['typo-adj'] = {
2    version   = 1.001,
3    optimize  = true,
4    comment   = "companion to typo-adj.mkxl",
5    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
6    copyright = "PRAGMA ADE / ConTeXt Development Team",
7    license   = "see context related readme files"
8}
9
10local setdimen          = tex.setdimen
11local isdimen           = tex.isdimen
12local setmacro          = tokens.setters.macro
13local expandmacro       = token.expandmacro
14
15local nuts              = nodes.nuts
16local tonut             = nodes.tonut
17local getattr           = nuts.getattr
18local getdepth          = nuts.getdepth
19local getlistdimensions = nuts.getlistdimensions
20local getexcept         = nuts.getexcept
21local getheight         = nuts.getheight
22local getid             = nuts.getid
23local getleader         = nuts.getleader
24local getlist           = nuts.getlist
25local getnext           = nuts.getnext
26local getprop           = nuts.getprop
27local gettail           = nuts.tail
28local getwhd            = nuts.getwhd
29local getwidth          = nuts.getwidth
30local setattr           = nuts.setattr
31local setdepth          = nuts.setdepth
32local setexcept         = nuts.setexcept
33local setheight         = nuts.setheight
34local setlink           = nuts.setlink
35local setlist           = nuts.setlist
36local setoffsets        = nuts.setoffsets
37local setprop           = nuts.setprop
38local setstate          = nuts.setstate
39
40local hpacknodes        = nuts.hpack
41
42local hlist_code        <const> = nodes.nodecodes.hlist
43local vlist_code        <const> = nodes.nodecodes.vlist
44
45local getbox            = nuts.getbox
46local takebox           = nuts.takebox
47
48local new_kern          = nuts.pool.kern
49local new_vlist         = nuts.pool.vlist
50
51local flattenleaders    = nuts.flattenleaders
52local traverselist      = nuts.traverselist
53local traverseleader    = nuts.traverseleader
54
55local a_adaptive        <const> = attributes.private("adaptive")
56
57local registervalue     = attributes.registervalue
58local getvalue          = attributes.getvalue
59local hasvalues         = attributes.hasvalues
60local texsetattribute   = tex.setattribute
61
62local v_right           <const> = interfaces.variables.right
63
64local adaptive          = nodes.adaptive or { }
65nodes.adaptive          = adaptive
66local enabled           = false
67
68----- enableaction      = nodes.tasks.enableaction
69
70local d_adaptive_width  <const> = isdimen("d_adaptive_width")
71local d_adaptive_height <const> = isdimen("d_adaptive_height")
72local d_adaptive_depth  <const> = isdimen("d_adaptive_depth")
73local d_adaptive_line   <const> = isdimen("d_adaptive_line")
74
75local function setadaptive(w,h,d,l,c,a)
76    setdimen(d_adaptive_width,w)
77    setdimen(d_adaptive_height,h)
78    setdimen(d_adaptive_depth,d)
79    setdimen(d_adaptive_line,l)
80    setmacro("m_adaptive_color",c)
81    setmacro("m_adaptive_alternative",a)
82end
83
84local methods = {
85    -- overlay
86    [1] = function(settings,parent,box)
87        local setups = settings.setups
88        if setups and setups ~= ""  then
89            local w, h, d, s, list = getlistdimensions(parent)
90            setadaptive(w,h,d,settings.rulethickness,settings.color,settings.alternative)
91            expandmacro("setup",true,setups)
92            local l = takebox("b_adaptive_box")
93            if l then
94                setlist(parent,setlink(l,new_kern(-getwidth(l)),list))
95            end
96        end
97    end
98}
99
100adaptive.methods = methods
101--------.set     = setadaptive
102
103local function handleuleader(n,grp,index,box,location)
104    local m = tonut(n)
105    local a = getattr(m,a_adaptive) or index -- index will become a selector
106    if a then
107        local settings = getvalue(a_adaptive,a) -- index triggers ...
108        if settings then
109            setstate(m,0) -- hm ...
110            local action = methods[settings.method or 1]
111            if action then
112                action(settings,m,tonut(box))
113            end
114        end
115    end
116    return n
117end
118
119-- callback.register("handle_uleader", handleuleader) -- will be frozen
120
121function adaptive.set(settings)
122    if not enabled then
123     -- enableaction("vboxbuilders","nodes.adaptive.handlehorizontal")
124     -- enableaction("vboxhandlers","nodes.adaptive.handlevertical")
125        callback.register("handle_uleader", handleuleader) -- will be frozen
126        enabled = true
127    end
128    texsetattribute(a_adaptive,registervalue(a_adaptive,settings))
129end
130
131interfaces.implement {
132    name      = "setadaptive",
133    actions   = adaptive.set,
134    arguments = {
135        {
136            { "setups", "string" },
137            { "method", "integer" },
138            { "mp", "string" },
139            { "location", "string" },
140            { "color", "string" },
141            { "rulethickness", "dimension" },
142            { "alternative", "string" },
143        }
144    }
145}
146
147-- The hlist leaders get done before we enter vpacking, so that is where the
148-- first call kicks in. Then we do a vpack (so one can indeed also adapt the
149-- ht/dp). After packing we know the glue and do the vlist leaders.
150
151-- This is now done via a callback but it is still experimental!
152
153local function handlehorizontal(n)
154    if hasvalues(a_adaptive) then
155        for _, t, _, l in traverselist(n) do
156            if t == hlist_code then
157                for m, _, _, ll in traverseleader(l) do
158                    local a = getattr(m,a_adaptive)
159                    if a then
160                        local settings = getvalue(a_adaptive,a)
161                        if settings then
162                            setstate(m,0)
163                            local action = methods[settings.method or 1]
164                            if action then
165                                action(settings,m,ll)
166                            end
167                        end
168                    end
169                end
170            end
171        end
172    end
173    return n
174end
175
176local function handlevertical(n)
177    if hasvalues(a_adaptive) then
178        -- not a list just a node
179        for _, t, _, l in traverselist(n) do
180            if t == vlist_code then
181                for m, _, _, ll in traverseleader(l) do
182                    local a = getattr(m,a_adaptive)
183                    if a then
184                        local settings = getvalue(a_adaptive,a)
185                        if settings then
186                            setstate(m,0)
187                            local action = methods[settings.method or 1]
188                            if action then
189                                action(settings,m,ll)
190                            end
191                        end
192                    end
193                end
194            end
195        end
196    end
197    return n
198end
199
200adaptive.handlehorizontal = handlehorizontal
201adaptive.handlevertical   = handlevertical
202
203interfaces.implement {
204    name      = "adaptivecheckbox",
205    arguments = "integer",
206    public    = true,
207    protected = true,
208    actions   = function(n)
209        local b = getbox(n)
210        if b and flattenleaders(b) > 0 then
211            if getid(b) == hlist_code then
212                handlehorizontal(b)
213            else
214                handlevertical(b)
215            end
216        end
217    end,
218}
219
220-- Excepts:
221--
222-- By using the adaptive uleaders we don't need a callback although we can hook it
223-- into the par node and just look at that one instead. However, now we can do it
224-- anyplace. Of course this only works per line unless we also look back but that is
225-- kind of tricky because then we also need to jump over glue and penalty nodes and
226-- compensate for the glue: a mess.
227
228nodes.adaptive.methods[2] = function(settings,parent,box)
229    local list = getlist(parent)
230    if list then
231        local except,
232              amount  = getexcept(box)
233        local left    = nil
234        local right   = nil
235        local current = except
236        while current do
237            local location = getprop(current,"exceptlocation")
238            if location == "left" then
239                left = current
240            elseif location == "right" then
241                right = current
242            end
243            current = getnext(current)
244        end
245        if not left then
246            left  = new_vlist()
247            right = new_vlist()
248            setprop(left, "exceptlocation","left")
249            setprop(right,"exceptlocation","right")
250            setlink(left,except,right)
251            setexcept(box,left)
252            except = left
253        end
254        -- todo: check for leaks !
255        local target = settings.location == v_right and right or left
256        -- wrapped in phantom
257        local n = getlist(list)
258        local nw, nh, nd = getwhd(n)
259        local h = hpacknodes(n)
260        if getprop(target,"except") then
261            nd = getdepth(target) + nh + nd
262            setlink(gettail(getlist(target)),h)
263        else
264            setheight(target,nh)
265            setlist(target,h)
266            setprop(target,"except",true)
267        end
268        setdepth(target,nd)
269        setexcept(box,except,nd > amount and nd or amount)
270        setoffsets(n,target == right and getwidth(box) or -nw)
271        setlist(list)
272        setlist(real)
273    end
274end
275
276