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