Conclusion : I give up :-)
We have three 'completed' approaches to the line numbering problem:
1. Jim Lambert's use of decimal-numbered-list style within the field
(has the limitation of not allowing the user's text to include list
styles, and some aesthetic constraints - but if you can live with those,
IMHO it's by far the best solution)
2. Jim's earlier version using a separate text field with only the line
numbers available. (Still has some aesthetic constraints, and noticeably
slower than (1) above)
3. My styled text-based version using space-below in a separate field
(works, no restrictions afaik, but *barely* fast enough for general use
- and certainly too slow for many uses).
And then we have another, not yet complete, solution - use Hermann's
script to find the limits on the line numbers which are visible, and
then something lik emy 'spacebelow' to set just those lines - so it has
the potential to be fast enough.
I set out to optimise the 'visibleLineNumber' function, and succeeded in
getting it down to approx 20% of the original version, by:
- reduce from 2 to 1 height calculations per iteration
- convert from recursive to iterative
- use Newton-Raphson style linear interpolation, with tweaks to avoid
the pathological end cases
- optimise the start values for finding the last visible line based on
the effective field height and min allowed text size
I was just feeling good about that, and getting ready to start on the
second half - displaying the line numbers for only those visible lines -
when I got this email from Hermann re. the bug in formattedRect, and the
need to use formattedHeight of chunk instead.
formattedHeight of line N to M of fld F has its own problems - it
misses out the space needed for the last line if that line happens to be
empty, and ignores spacebelow - but that can be easily avoided
function getAccurateFormattedHeight pFld, N, M
put the formattedheight of line N to M of fld pFld into h
if line M of fld pFld is empty then add the formattedheight of line
M of fld pFld to h
add the spacebelow of line M of fld pFld to h
return h
end getAccurateFormattedHeight
Then I started testing on "hard" fields - and only then did I discover
the real problem - formattedHeight of a chunk is just too slow. It takes
50-100 msecs to get such a chunk from a large-ish field (30K lines,
heavily styled, from the dictionary); so even though the number of
iterations was down to between 7 and 12, that still represents over a
second of elapsed time - which is just too long to be feasible.
And now that I have created this test case, it shows that solution no. 3
above is also too slow.
So, I give up :-)
-- Alex.
On 29/03/2017 11:48, hh via use-livecode wrote:
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
_______________________________________________
use-livecode mailing list
use-livecode@lists.runrev.com
Please visit this url to subscribe, unsubscribe and manage your subscription
preferences:
http://lists.runrev.com/mailman/listinfo/use-livecode
_______________________________________________
use-livecode mailing list
use-livecode@lists.runrev.com
Please visit this url to subscribe, unsubscribe and manage your subscription
preferences:
http://lists.runrev.com/mailman/listinfo/use-livecode