Line numbers for soft-wrapped styled text?
hh
hh at hyperhh.de
Wed Mar 29 06:48:26 EDT 2017
Alex,
before you waste valuable time:
The formattedRect can NOT be used in LC 7/8/9, because of the
(2^15 div 2)-limit for coordinates is active for that.
So the algorithm works in LC 6.7.11, but the results are sadly
'extremely' wrong in LC 7/8/9:
Crossing the 'limit' with a vertical coordinate jumps to negative
values for these coordinates. No chance to repair.
Using instead the formattedHeight of line 1 to N minus the
vscroll works. But it is not exactly doable if the last line is
empty (this is not added to the formattedHeight) = your space-below
problem -- a bug, TMHO.
I found another "dirty" but fast way to get pixel-exact line
positions for any field in LC 6/7/8/9. One could perhaps use it
until the text measuring problem is solved.
One needs a field that is transparent, is not threeD and has no
border (use instead an opaque rectangle with border as background).
Then set the hgrid to true (this has no visual effect if using as
borderColor the backColor of the graphic behind the field, for
testing I set the borderColor to red).
Now make a snapshot from the field and walk through the maskData
to search all such rows in the image that have a fully opaque
line. This is unique as long as the field has left and right
margins > 0.
This search in the image is *very fast* (< 2 ms), only taking the
snapshot and getting the maskdata needs some time (around 70 ms in
sum for 414x736 pixels).
A script that works here correctly with my tests is attached below.
It needs here in LC 9 about 120 ms for 4000 lines of heavily styled
text from the dictionary in a field of iPhone6 size (414x736).
Please change/improve/optimize it, especially the search part. This
is still the 'simple' binary search.
Hermann
You need a field "TEXT" (transparent etc, see above) and a
template-num-field "n00" (see my settings below as example).
Fields for feedback: "Range", "timing1", "timing2", "info"
===== [1] The field's script
on textchanged
updateNbs2
end textchanged
on scrollbardrag
updateNbs2
end scrollbardrag
===== [2] The numbering script (100 lines)
local nn="lineNumbers",l0,t0,b0,w0,h0,sw0,v0
local rg="TEXT", fw=32 -- num-field width
on updateNbs2
lock screen; lock messages
put the millisecs into m0 ---- start
put the top of fld rg into t0
put the bottom of fld rg into b0
put the left of fld rg into l0
put the width of fld rg into w0
put the height of fld rg into h0
put the vscroll of fld rg into v0
put the scrollbarwidth of fld rg into sw0
set the properties of the templatefield to \
the properties of fld "n00" -- see below
if there is a grp nn then delete grp nn
create grp nn
set lockloc of grp nn to true
set opaque of grp nn to true
set backColor of grp nn to "204,204,204"
set lockloc of grp nn to true
set width of grp nn to fw
set height of grp nn to the height of fld "text"
set topleft of grp nn to l0-fw, t0
set layer of grp nn to 1
put the millisecs into m1 ---- diff1
put visibleTextLines()-1 into tL
put the millisecs into m2 ---- diff2
put getLocs() into g
put the millisecs into m3 ---- diff3
put "Lines: " & (tL,tL-1+the num of lines of g) \
into fld "range"
repeat for each line L in g
put item 2 of L into i
put ("n"&i) into ni
if there is a fld ni then delete fld ni
create fld ni in grp nn
set left of fld ni to l0-fw+2
set top of fld ni to i-10
put tL into fld ni
add 1 to tL
end repeat
reset the templatefield
put the millisecs into m4 ---- diff4
put (m4-m0 &"="& m2-m1 &"+"& m3-m2 &"+"& m4-m3+m1-m0) & \
" ms" into fld "timing"
unlock screen; unlock messages
end updateNbs2
function getLocs
put the millisecs into m0 --------------------- start
set topleft of img rg to \
(5+the right of fld rg, the top of fld rg)
put w0-sw0 into w1
put the millisecs into m1 --------------------- diff1
export snapshot from fld rg to img rg as PNG
put the millisecs into m2 --------------------- diff2
put the maskdata of img rg into mData
put the millisecs into m3 --------------------- diff3
put NumToByte(255) into c1
repeat 1+log2(w1)
put c1 after c1
end repeat
put byte 1 to w1 of c1 into c00
put the millisecs into m4 --------------------- diff4
repeat with i=1 to h0
put (i-1)*w0 into i0
if byte i0+1 to i0+w1 of mData is c00
then put cr&(0,t0+i) after s
end repeat
-- avoid overlapping:
-- put (0,min(t0,10+item 2 of line 1 of s)) before s
put cr & (0,max(h0+t0,10+item 2 of line -1 of s)) after s
put s into fld "info"
put the millisecs into m5 --------------------- diff5
put (m5-m0 &"="& m2-m1 &"+"& m3-m2 &"+"& m5-m3+m1-m0) & \
" ms" into fld "timing2"
return char 2 to -1 of s
end getLocs
function visibleTextLines
lock screen; lock messages
put the selectedChunk into sc
put the scrollbarWidth of fld rg into sw
put the margins of fld rg into m
put (m,m,m,m) into m -- now we have at least 4 items
put the num of lines of fld rg into n
put findTopLine(v0+t0-1+item 4 of m,1,n) into L1
return L1-1
end visibleTextLines
function findTopLine x,n1,n -- x=top pixel, n1=start, n=max
put n1+((n-n1) div 2) into m
if (the formattedHeight of line 1 to (m+1) of fld rg) >= x then
if (the formattedHeight of line 1 to m of fld rg) < x then
return m+1
else
if m <= n1 then return n1
else return findTopLine(x,n1,m-1)
end if
else
if m >= n then return n
else return findTopLine(x,m+1,n)
end if
end findTopLine
===== [3] This may be used for the "template"-num-fld "n00"
-- set locktext of fld "n00" to true
-- set dontwrap of fld "n00" to true
-- set traversalOn of fld "n00" to false
-- set height of fld "n00" to 12
-- set width of fld "n00" to fw
-- set margins of fld "n00" to 4
-- set textalign of fld "n00" to "right"
-- set opaque of fld "n00" to false
-- set threed of fld "n00" to false
-- set showborder of fld "n00" to false
-- set textsize of fld "n00" to 10
-- set foreColor of fld "n00" to "0,0,255"
=====END_OF_SCRIPT
More information about the use-livecode
mailing list