TextArea - LinenumAtCharPos etc.

Correct me if I’m wrong… but I thought that LineNumAtCharPos returned the linenumber at char position :slight_smile: … by this I mean if you have a Really long line that wraps in the textarea… and you click in the middle… it should return the linenumber of the beginning of the line … as in… “the character you just selected in on the physical line that is X number of linebreaks from the beginning of the document”

What I am seeing is each wrap is counted… so if the TextArea can show 50 characters wide and your text is 300 characters (no EOL), then it counts as 6, when it should count as 1…

What I am trying to do it go down the left side of a text area and paint line numbers into a canvas

Paint Event of Canvas

lastLine=(-1)
		for y=0 to g.Height step h
				cp=theEditor.CharPosAtXY(1,y)
				lineNum = theEditor.LineNumAtCharPos(cp)
				if lastLine<>lineNum then g.DrawString str(linenum),1,y+g.TextAscent
lastLine=lineNum
		next y

but it is writing a number on each row, when I know the text is wrapped…

So the question is… am I doing something wrong? or is my method flawed because the function doesn work like I thought it did.

Basically “what is a line” in this context?

The function does not do what you think it does. It returns the line # of the textarea, not the line # of the text.

Or as Inigo Montoya would say, You keep using that function. I do not think it does what you think it does.

came to that sad realization… What a TA considers a line is the text the fits left to right in the view… Where a Paragraph is the entity that I think I need… but the only way to do that is to count from the beginning of the document each time… and that can be slow… since most of the “paragraphs” will be one “line”

this is what I wanted to do… and this was done with a NSTextView (which is supposedly what a TextArea for macOS is)

You want to count paragraphs, not lines. The function is called LineNumAtCharPos. The problem is not the function but your perception.

You may need TextInputCanvas which extends beyond the basic functionality of TextArea.

I was going to say that you could InStr EndOfLine and check in which row there is an EndOfLine so you could only write a line number after that.

But after some tests, I think there is a bug with LineNumAtCharPos. From https://documentation.xojo.com/index.php/TextEdit.LineNumAtCharPos

[quote]Characters are numbered consecutively with the first character numbered 1. The first line is numbered zero. Used with ScrollPosition, this lets you scroll the control to a particular place in the text.

The line number reflects line breaks from both hard line breaks in the text and soft line breaks caused by word-wrapping within the control.[/quote]

I made a sample project with 1 TextArea, 1 Listbox and 1 PushButton, on TextArea Open event:

me.Text = EndOfLine + EndOfLine + EndOfLine + EndOfLine

On PushButton Action event:

Listbox1.DeleteAllRows Listbox1.AddRow(TextArea1.LineNumAtCharPos(1).ToText) Listbox1.AddRow(TextArea1.LineNumAtCharPos(2).ToText) Listbox1.AddRow(TextArea1.LineNumAtCharPos(3).ToText) Listbox1.AddRow(TextArea1.LineNumAtCharPos(4).ToText)

By reading the docs, I expected:

0 1 2 3

On Mac I get:

0 0 1 2

On Windows I get:

0 1 2 3

On Linux I get:

1 2 3 4

Yes I want to count paragraphs (as I said above)… but there is no function to do so, with out counting from the beginning every time[quote=414018:@Alberto De Poo] and soft line breaks caused by word-wrapping[/quote]
Alberto… its the “soft breaks” the make this “difficult”

You want to do something like this?

I don’t have experience with canvas so I used 2 TextAreas

well yeah… although the synch is kind of off… show me what you did? Perhaps I can make something more of it

Well, I put the code on resized and not resizing and I did it on Windows because of the bug I mentioned above. Also Windows has a bug with line endings on TextAreas, instead of 0D0A they are just 0D, that’s why I use EndOfLine.Macintosh there.

I’m sure you can make a much better code, basically what I did:

  • Use InStr(TextArea2.Text, EndOfLine.Macintosh) to find the first char that is EndOfLine
  • Check in what row (line) is in the TextArea
  • Put EOL on TextArea1 (left) to have blanks
  • Have a counter to know when is paragraph 2, 3 and so on

The code is:

[code]TextArea1.Text = “”
// using EndOfLine.Macintosh because the TextAreas on Windows just use 0D and not 0D0A
Dim charPos As Integer = InStr(TextArea2.Text, EndOfLine.Macintosh)
Dim cp As Integer = 1
Dim linea As Integer
Dim ln As Integer = 1

// write 1 in the first row always
TextArea1.AppendText(cp.ToText)

While charPos > 0
// found and EOL, we have another paragraph
cp = cp + 1
// find in which line (row) is the EndOfLine (0 based)
ln = TextArea2.LineNumAtCharPos(charPos) + 1
// I start at linea + 1 to Append an EOL to the number on TextArea1
For j As Integer = linea + 1 To ln
// add EOL
TextArea1.AppendText(EndOfLine.Macintosh)
linea = j
Next
// Append the paragraph number
TextArea1.AppendText(cp.ToText)
// find another EOL
charPos = InStr(charPos+1,TextArea2.Text,EndOfLine.Macintosh)
Wend[/code]

I just changed the code to make it work around the mac bug, it makes sure that is not putting 2 numbers in the same line (at least an EOL between them):

[code]TextArea1.Text = “”
Dim charPos As Integer = InStr(TextArea2.Text, EndOfLine)
Dim cp As Integer = 1
Dim linea,lineaorig As Integer
Dim ln As Integer = 1
Dim ciclo As Integer

TextArea1.AppendText(cp.ToText)

While charPos > 0
cp = cp + 1
ln = TextArea2.LineNumAtCharPos(charPos) + 1
// make sure we don’t append a number to another number, must be at least 1 EOL (mac bug)
If ln = lineaorig Then ln = ln +1
lineaorig = ln
For j As Integer = linea + 1 To ln
TextArea1.AppendText(EndOfLine)
linea = j
Next
TextArea1.AppendText(cp.ToText)
charPos = InStr(charPos+1,TextArea2.Text,EndOfLine)
Wend[/code]

Because Linux start with 1 instead of 0, the code will need to change to make it work. I’ll report the bug tomorrow.

Please be kind, the code works (in my tests), but I don’t have much experience, I bet it can be cleaned/fixed.

Edit: I put the code on a Method, now I call that too from TextArea2 TextChange event. On Linux when I paste the paragraphs the numbers on the left are in wrong position, if I add a return after the paste or resize the window then the numbers are set correctly. This doesn’t happen with Windows or Mac.

I changed the code to make it work on Windows, Mac and Linux. On a quick test it worked:

[code]TextArea1.Text = “”
#If TargetWindows Then
// TextAreas on Windows use EndOfLine.Macintosh and not the standard Windows EndOfLine
Dim EOL As String = EndOfLine.Macintosh
#Else
Dim EOL As String = EndOfLine
#EndIf
Dim charPos As Integer = InStr(TextArea2.Text, EOL)
Dim cp As Integer = 1
Dim linea,lineaorig As Integer
Dim ln As Integer = 1
Dim ciclo As Integer

TextArea1.AppendText(cp.ToText)

While charPos > 0
cp = cp + 1
#If TargetLinux Then
// it looks like on LInux it is 1 based
ln = TextArea2.LineNumAtCharPos(charPos)
#Else
// Windows and Mac is 0 based
ln = TextArea2.LineNumAtCharPos(charPos) + 1
#EndIf
#If TargetMacOS Then
If ln = lineaorig Then ln = ln +1
lineaorig = ln
#EndIf
For j As Integer = linea + 1 To ln
TextArea1.AppendText(EOL)
linea = j
Next
TextArea1.AppendText(cp.ToText)
charPos = InStr(charPos+1,TextArea2.Text,EOL)
Wend[/code]

That’s by design and documented.

Sorry, didn’t know that and I can’t find where it say that. I’ll search again.

Maybe because I filed a bug report with 2 entries they just said verified and I assumed there is a bug on Windows TextArea:
feedback://showreport?report_id=53606
From that bug report:

  • if you use EndOfLine.Windows on. a Windows TextArea, instead of 0D0A only 0D is used
  • if you use EndOfLine.Unix on a Windows TextArea, instead of 0A, only 0D is used
    On Mac and Linux:
  • if you use EndOfLine.Windows on a TextArea, 0D0A is used
  • if you use EndOfLine.Unix on a TextArea, 0A is used

I will report the bug above in 2 cases, one for Mac and one for Linux

Ok… I woke up in the middle of the night and realized that I was making a major mis-assumption is my requirements.

  • The textarea SHOULD not wrap to begin with. Now that will obviously make determining linenumbers much easier.
    but that now begs a follow on question.

Since the scroll events for a TextArea are not exposed (ie. Valuechanged) has anyone figured out a way to

  • disable the built in TextArea Scrollbars
  • but still maintain WordWrap = FALSE
  • and use an external HScroll and VScroll that remains properly synced to the TextArea

To make it easier the TextArea will ALWAYS (in this situation) contain a single size, mono-spaced font.

If I understand correctly, you want something like the IDE code editor, right?

Edit: Sorry I can’t help you with that, too complex for me at this time.

[quote=414102:@Alberto De Poo]If I understand correctly, you want something like the IDE code editor, right?
[/quote]
exactly… the syntax highlighting part I have handled… the code folding I will do without

and CustomEditField is not an option… it won’t survive thru the next macOS without a major overhaul

Maybe a TextInputCanvas. While it means you have to draw everything yourself, it gives you the control to do whatever you want and handles the various input mechanisms on macOS, Windows & Linux.

TextInputCanvas can be found here:

https://github.com/xojo/TextInputCanvas

I am assuming that is the magic control that everyone talks about, but for which there is no documentation or examples