Bug? Unreliable "tab" in text strings

I’m trying to get a reliable Tab in my text strings, but I often (not always) get an “n” instead of a Tab on the screen. In the two screens below, I changed NOTHING, I simply reloaded the channel. There is no rhyme nor reason!

[upload|8jQtpqV3j4S7hOOYO0h7iQ==]

[upload|naT5jseSMbFSGjyQcn97ew==]

I’ve tried actual Tabs in my text (by pressing Tab on my keyboard), and I’ve tried the tab character via adding Chr(9) to the string. Both ways give the same results - sometimes works, sometimes gives an “n”, and I’ve even seen it once give a “(” in place of every Tab instead of the usual “n”.

It’s driving me crazy. I need to way to absolutely guarantee this will not happen. One way is to simply forego the tabs, but the paragraphs look so much better with them. There has to be a way.

Recent testing is with Roku 3 4200X, v7.0, build 9021

Is there a reason you can’t just use spaces to indent the first line of each paragraph? Seems like that would be an easy and reliable solution.

That was the first thing I tried - they are just ignored. Am I doing it wrong? I tried actual spaces, I tried building the string starting with Chr(32) + Chr(32) + Chr(32), nothing worked. If you have used spaces and it works I would love to do that.

“Komag” wrote:
That was the first thing I tried - they are just ignored. Am I doing it wrong? I tried actual spaces, I tried building the string starting with Chr(32) + Chr(32) + Chr(32), nothing worked. If you have used spaces and it works I would love to do that.
Interesting. I’ve noticed that several screens also seem to trim off ellipses (…) as well, which is extremely frustrating. Have you tried a non-breaking space? Chr(160)

YES, that does the trick! No more random “n”! Thank you!

How did you find that code#? I looked up ascii tables, but I guess that must be wrong.

I did a Bing ( :oops: err… Google? ) search for “Non-breaking space character code”.   is a pretty standard web character that allows for adding a space without affecting the wrapping of a line. Thought it might work for your purposes. Glad it did!

“Komag” wrote:
I’m trying to get a reliable Tab in my text strings, but I often (not always) get an “n” instead of a Tab on the screen. In the two screens below, I changed NOTHING, I simply reloaded the channel. There is no rhyme nor reason!

Recent testing is with Roku 3 4200X, v7.0, build 9021
This is not a helpful bug report.
What exactly were you testing? Which component or function used to output text? No clue.
It seems as if someone is stripping whitespace at string ends (akin to ifStringOps.trim() but not quite) - and wrongly at that, give the phantom “n”.
Can you show a 5-10 line code example that can reproduce the issue?

My text system is a little complicated, I wasn’t exactly trying to submit a good bug report, but rather to see if anyone had experienced anything like this (based on symptoms) or if anyone had ideas. Luckily, the non-breaking space worked like a charm.

I suppose there are two issues:

  1. Leading spaces are always trimmed
  2. Tab character is not always handled correctly (whether actual Tab or Chr(9)
    This second case has some more details:
    I tried Chr(11) VT vertical tab, Chr(12) FF form feed, and a couple others that I can’t remember. Interestingly, they all behaved like Tabs, they made a few spaces. And they all SOMETIMES were replaced with "n"s. I could exit my channel and restart it 10 times, and get 8 times correct indentations and 2 times the "n"s, it was totally inconsistent and unreliable.

Here is the Function I was working on:

FUNCTION longTexts(title AS STRING) AS STRING ' trig by initBook(1) ' Chr(0) null, Chr(9) Tab, Chr(10) LF, Chr(13) CR, Chr(32) sp, Chr(34) dbl quotes, Chr(11) VT, Chr(12) FF, Chr(160) nbsp
'	TB		= "" + Chr(9)
	TB		= Chr(9)
'	TB		= "" + Chr(32) + Chr(32) + Chr(32) ' This doesn't work, no spaces or "tab" appears
'	TB		= "" + Chr(0) + Chr(32) + Chr(32) + Chr(32) + "" ' This doesn't work either
'	PH		= "" + Chr(10) + Chr(10) + Chr(9)
'	PH		= Chr(10) + Chr(10) + Chr(11) + Chr(32) + Chr(32) + Chr(32) + Chr(0) ' Vertical Tab works! But just as prone to "n" bug ' BUT, this retained the spaces after...
'	PH		= Chr(10) + Chr(10) + Chr(11) ' Just a regular Tab size, prone to "n" bug
'	PH		= "" + Chr(10) + Chr(10) + Chr(32) + Chr(32) + Chr(32)
'	PH		= "" + Chr(10) + Chr(10) + Chr(0) + Chr(32) + Chr(32) + Chr(32) + ""
'	PH		= Chr(10) + Chr(10) + Chr(12) + Chr(32) + Chr(32) + Chr(32) + Chr(12) ' Form Feed works, interesting... ' Nope, Form Feed seems prone to "n" bug too
		' but at least between the "n"s are the spaces, maybe there is some other blank character I can stick spaces inbetween
'	PH		= Chr(10) + Chr(10) + Chr(12) ' Nope, Form Feed seems prone to "n" bug too
	PH		= Chr(10) + Chr(10) + Chr(160) + Chr(32) + Chr(32) + Chr(32) + Chr(160) ' Wow, Chr(160) nbsp works, thanks to TheEndless
'	PH		= Chr(10) + Chr(10) + " " + Chr(32) + Chr(32) + Chr(32) + Chr(0)

	texts = {
		intro:			TB + "This is the story of Elendia's Esteem, the highest grace and favor ever bestowed upon a human, only granted once in all the ages known to the recorders." + PH + "In the Kingdom of Evermore, many gods and goddesses compete to influence the lives of the people, to gain their loyalty and worship, and to guide the events of the heavens in their own favor. Elendia is one such diety, a member of the Prime Synod, the ancient council of the gods formed by common consent of those oldest of beings, long ago in a time of chaos and disorder." + PH + "With her rivals Thanor, Kyrindia, and Loxus, Elendia agreed to divide the world into four kingdoms, and she named her domain Evermore, after the longing in her heart to lead the people toward eternal growth and divine progression, never to end, becoming greater evermore." + PH + "In time, peace and order coalesced upon the world, to the satifaction of the Prime Synod, and it was into this order that the Untried emerged. They are the lesser gods, unversed and untested, powerful but limited. They never knew a world of chaos, and their followers come and go. The Untried dain to compete with their elder deities, and cause their mischief as they can." + PH + "The oldest manuscripts of the recorders, copies of copies, tell us the tale of prince Albrecht. He was the youngest son of the king, and unlauded, so he desired to prove his valor." + PH + "In search of a task he might accomplish, he sought out the wisdom of oracles of his day, whom we would now call sages, but from the record it appears they harbored a stronger connection to the heavens than that." + PH + "Albrecht was instructed not to perform some great feat of strength or dexterity, but rather was told to lift others, to become a guidestone in the lives of the many, to better the position of the common man." + PH + "Time passed, and Albrecht pondered how he might fulfill the quest. This feat was like no other he had heard of, yet it rang true in his heart." + PH + "One day, while fasting and meditating atop the highest tower of the kingdom, the Skyward Hand (remnants of its foundation are still visible to this day in Hightowne), he beheld in vision the people of the kingdom. He saw them taking up arms against a dark and shadowy foe, layered in ranks upon the hillsides below them, his people formed all about the top." + PH + "From the earliest shades of dawn to the faintest light of dusk, his people cast down the devils from the hill top, again and again. An endless onslaught, repelled, returning, repelled again." + PH + "As he watched the fighters clash, he saw that the black shapes of the enemy seemed to match the outlines of their brighter counterparts. Indeed, upon close inspection, he believed it was in fact true that each man fought an exact copy of himself, only without light or visage." + PH + "In an instant Albrecht knew the meaning, that each man fought against his baser half, his low instincts, the parts of him that would drag him down and see him mired in self-indulgence. Leaning forward in his trance, he focused his mind forward to the end of the battle. He saw his people begin to prevail, their light shining forth ever more brightly, their dark foes diminishing day by day through the struggle." + PH + "Then, an unexpected thing occurred. Where each shadow fell, a tree sprung up. In his vision, time passed in a strange manner - the trees grew praeternaturally fast, while the men and devils fought impossibly slow. It was such that limbs of men and trees moved at the same speed; struggle matched growth." + PH + "Albrecht witnessed many individuals felling some of the trees and carving out new weapons and shields from the wood. They built homes and lives from it. The whole foundation of their society began to grow by it, and as this occurred, fewer and fewer dark shapes emerged from the valleys below." + PH + "After eons of fighting, living, conquering, his people prevailed. Darkness retreated, leaving a shining city on a hill in its wake. Tall forests of strong trees covered the land, teeming with life and sustenance, while men labored and took joy in their work. Women and children crafted and played and loved, and all were whole and happy." + PH + "The vision faded away, and the young prince Albrecht felt his own form wavering. He was weak and drained, and could not stand. Servants bore him to his room, where he was attended for three days while he regained his strength."
	
	}
	RETURN texts[title]
END FUNCTION

After that the big string goes to get split up in to many small lines and positioned on the page according as they fit, divided into multiple pages that can be flipped with right/left. It’s my in-game book system.

longTexts() is called by initBook()

FUNCTION initBook(bk) ' trig by interactCheck(1), noted by objctInfo(1) "book", trig ind by useItem(1)
	'? "initBook() running"
	m.cAA.mainMenu.Lv	= -1								' Turn off main menus ' This would happen if reading a bk fom inven
	title					= textDatabase(bk.dscr, 1)		' Get the book's title from the text database
	text					= longTexts(title)					' Get the actual book long-text
	dbook				= m.drAA.book
	dbook.book		= TRUE
	w						= 7*64 + 16
	lines					= splitBlockText(text, w, m.drAA.font)
	pages				= []
	WHILE lines.Count() > 11								' Take out first 11 lines and put them into a "page"
		page = []
		IF lines[ 0] = "" THEN lines.Shift()				' Take out any blank line at the top ' This could possibly reduce lines.Count to exactly 11 (if it was 12)
		FOR i = 1 TO 11
			page.Push(lines.Shift())
		END FOR
		pages.Push(page)
	END WHILE
	IF lines.Count() > 0 THEN pages.Push(lines)	' At this point there should be <= 11 lines left for the last page, and there could be 0 if a blank line was removed from previous page top
	dbook.pages		= pages
	dbook.pg			= 0
	dbook.pgs			= pages.Count() -1
	setBookPages()
	initBookSprites(dbook)
	'SOUND ' play "open book" sound
	dbook.theBook	= bk												' Store bk so exitBook() can check for any .linkTo deact
	IF bk.linkTo <> INVALID THEN doLink("on", bk, y, x, rm)	' If book has a linkTo, do it ' Send trig as "on" (act)
END FUNCTION

And here is splitBlockText() that RokuMarkn gave to me (I’ve never fully understood it - maybe this is stripping out leading spaces?)


FUNCTION splitBlockText(text AS STRING, width AS INTEGER, font AS OBJECT) AS OBJECT ' trig by prepText(1), drawConvo(1), initBook(1) ' From RokuMarkn, updated to handle New Lines w/ Chr(10)
		' Ascii 10 is New Line or Line Feed. Enter into strings like: "Hello."+Chr(10)+"Goodbye." ' Don't use "/n" because both / and n are delims, so all n's cause split!
	'timer = createObject("roTimespan") ' For measuring performance
	splitTextLines = []
	linePos = 0
	WHILE TRUE
		WHILE text.Mid(linePos, 1) = " "		' Mid(start_index as Integer, num_chars as Integer) as String. Ex: s.Mid(0, 1) returns the first letter of s, while s.Mid(2, 2) returns the 3rd and 4th letters
			linePos = linePos +1
		END WHILE
		endLine = linePos							' Set endLine to the current position, and test it below
		' Output line starts here; step thru words.
		WHILE TRUE
			IF endLine >= text.Len() THEN
				IF endLine > linePos THEN splitTextLines.Push(text.Mid(linePos, endLine - linePos))
				'? "SplitBlockText() took " timer.totalMilliseconds()
				RETURN splitTextLines
			END IF
			newline = (text.Mid(endLine, 1) = Chr(10)) ' If the second two parts are equivalent, newline will be TRUE
			IF NOT newline THEN e = endWord(text, endLine)
			IF newline OR font.GetOneLineWidth(text.Mid(linePos, e - linePos), width) >= width THEN
				splitTextLines.Push(text.Mid(linePos, endLine - linePos))
				linePos = endLine
				IF newline THEN linePos = linePos +1
				EXIT WHILE
			END IF
			endLine = e
		END WHILE
	END WHILE
END FUNCTION

FUNCTION endWord(text AS STRING, index AS INTEGER) AS INTEGER 'trig by SplitBlockText(1), from RokuMarkn on the dev forums
	FOR p = index TO text.Len()
		IF text.Mid(p, 1) <> " " THEN EXIT FOR
	END FOR
	FOR p = p TO text.Len()
		IF text.Mid(p, 1) = " " OR text.Mid(p, 1) = Chr(10) THEN EXIT FOR ' Handling for New Lines
	END FOR
	RETURN p
END FUNCTION

After trying again to understand how splitBlockText() is working, I think it is indeed the culprit for stripping leading spaces (which is usually a good thing for what it does). So that’s one mystery solved (I think). :oops:

But the Tabs and other characters SOMETIMES making a tab indentation and SOMETIMES putting in an “n” is still a baffling mystery, especially the one time I saw “(” for them all instead of "n"s, it just seems buggy. :?

But for me, the issue is resolved, so thanks again. :grinning_face_with_smiling_eyes:

Yeah, I wondered about what would happen with a too-long word, but I never tried it!

BTW, congrats your 2000th post! :mrgreen:

I’ve updated splitBlockText() as you suggested, works great, now I can simply add a few spaces wherever I intent a new paragraph to start, nice! Thanks :grinning_face_with_smiling_eyes:

“EnTerr” wrote:
Regarding the mysterious “n”-fication - i suspect somewhere there is a left-over code from your experiments with viewtopic.php?f=34&t=84213#p481933
If i were troubleshooting this, first step when this happens during a run would be to check if the 1st char in the splitTextLines

Yes, I thought of that before and did Ctrl-C to see the text under the hood - no “n” in sight. That, plus the fact that the bug was unpredictable and inconsistent between channel loads, plus the fact that the "n"s only showed up when I tried to use leading Chr(9) or Chr(11) or Chr(12) or a few others tells me it’s a bug with how those characters are handled in the Roku.

“Komag” wrote:
Yeah, I wondered about what would happen with a too-long word, but I never tried it!
Well try it out, what you waiting for? Mine was just an armchair theori… um, i mean “wetware static analyzis” :idea:

BTW, congrats your 2000th post! :mrgreen:
It was? Huh, true. Thank you - and very observant of you! Looking at the sidebar, seems soon i will be celibating 5 years in this forum too. Wheel in the cake! :slightly_smiling_face:

Interesting - It creates a blank line for every letter until the remaining letters do fit, then puts them on a line and then it moves on. I tried PNEUMONOULTRAMICROSCOPICSILICOVOLCANOCONIOSIS, and I ended up with about 20 blank lines and then “OPICSILICOVOLCANOCONIOSIS”. So that could appear ugly if I really use a too-long word, but at least it will never fully break the system. I’ll probably leave it as-is, I don’t expect to ever need a very long word in a narrow text window.

(When I tested it all lower case, more letters fit - about 15 blank lines and then “icroscopicsilicovolcanoconiosis”)

Oh? Uh… Ahaaa!
it’s the replacing of “IF newline THEN linePos = linePos +1” with “linePos = linePos + 1” that “fixed” it. beneficial side effect.
hahahaha… my analysis of the original was right but did not consider the change - now in the loop it always advances the position by 1. And yes, it’s better to screw formatting than to bomb