2006/07/05

How to handle text and keep your sanity

Showing text in simple ways is very easy in XUL. You have two tags at your disposal: label and description. Even though they are implemented just the same, their semantic is different, so you should use them accordingly.
Labels are supposed to be used for the small bits of text you see all around an interface: menu labels, titles, form fields, etc. They're usually short (2 or 3 words) and need to be shown without breaks. And that's what a label will do. The following:
<label value="Some text here" />
Will display whatever you type in the value attribute in one line. The Gecko engine will try its best to show the full label, and in the very worst case, it will be cropped. No line breaks. No way, no how. And it makes sense to have such an element in your interface language. You don't want things like menu items or titles to be multiline. Things would get pretty ugly.
Description elements are used for longer texts. They are also used for text that could be short or long, and you just don't know. This is the tag to use when you're adding explanations or other pieces of text that may break into multiple lines. You'll find some of those in the Options window. Now here's where the quirks start to appear.
<description value="Some very, very long text here" />
Will this text break under the right circumstances? No. Using the value attribute will always produce a single-line block of text. It's exactly the same as using the label element. What you want to do is the following:
<description>Some very, very long text here</description>
That breaks, when the available width is insufficient. As a general rule of thumb, you should remember to always use the label element with the value attribute, and always use the description element with the inner text node. Use label when you don't want breaks. Use description otherwise.
Now, how about introducing text with breaks? That's a little different. Doing the following makes no difference:
<description>Some very, very
long text here</description>
All breaks and white space is collapsed into a single white space, just like with HTML. We're missing something here. After a little quest for information I found a solution in the following blog post: XUL Description Revisited. A few solutions are reviewed, and the most elegant by far consists on doing the following:
<description style="white-space: pre;">Some very, very
long text here</description>
A little stylin' saved the day :). This is the same as using the HTML "pre" tag, so have to be careful about all white space and breaks in the text, including before and after the text.

Dynamically generated text

So far things have been fairly easy. But sometimes you'll need to handle text in more complicated ways, and things get very tricky. This is something I had to deal with a lot of frustration while developing a relatively complex extension. This issue relates directly with templates, but may also be relevant for XBL.
Creating text with breaks on a template proved to be quite a headache for me. I ran into a bit of a paradox. It's easy to make an attribute of an element be assigned dynamically from the datasource:
<label value="rdf:http://mypage.com/my-schema#some-value" />
Clearly this won't work with the description element, at least not in the way we need it. But worry not, template designers thought of this and came with a rather elegant solution. You can use a "text" element to represent a text node. So all you need to do is this:
<description>
<text value="rdf:http://mypage.com/my-schema#some-value" />
</description>
That's not so hard, now is it? This is where the irony kicks in: the "text" element generates a text node that doesn't break! I couldn't help but laugh... oh, well. I tried a million different ways to get the damn thing to break, but I just couldn't. When is this element useful then? Maybe to do stuff in HTML, I don't know.
Back to the drawing board. For a while I used a workaround and just divided the text into multiple single-line description elements (should've been label according to my own rule, :P), but this just didn't cut it. My extension required the text field to have relatively arbitrary widths, so this was not an acceptable solution.
So it came down to using the multi-purpose textbox element. I left it as a last resource because I see it more as an input element, so I feel it's kind of a bad practice to use it for read-only text. But this was my last hope, so I had to give it a try. And here it is, in all its wonder:
<textbox class="plain" style="background-color: transparent;" flex="1"
multiline="true" readonly="true"
onoverflow="this.height = this.inputField.scrollHeight;"
DOMAttrModified="if(event.attrName == 'value') this.value = event.newValue; return true;"
value="rdf:http://mypage.com/my-schema#some-value" />
This yields exactly what I needed: a read-only, vertically flexible text field that wraps intelligently. Nirvana.
The class is set to "plain" so that the border doesn't show. The background color is set explicitly to counter the ugly color that's set for read-only textboxes. The onoverflow event makes the textbox adjust its height dynamically, according to the text it contains. Finally, the DOMAttrModified keeps the contents of the textbox consistent with the value that's set by the template. Changing the value attribute does not change the displayed value automatically. I'll dig a little deeper into this in a future post.
So there you have it. Labels, descriptions and textboxes. There's always a way, though not always pretty.

Labels: , , , , , ,


Comments:
A recent finding made by someone else in another project I'm working on is very relevant to the topic at hand. it seems that the label / description / description-with-text-node behavior extends to certain CSS properties associated with white space.
In this particular case, the CSS "word-spacing" property will not work unless applied to a description (or label, but not recommendable) element with a text node child instead of the "value" attribute. This probably extrapolates to other properties, so look out for those.
 
Did you consider filing a bug for the issue with <text>?
 
I figured it was (1) a design choice or (2) a known bug. I assumed templates were pretty common, and using text nodes shouldn't be so rare, but I may have been mistaken.
 
It may be a known bug, but it's always a good idea to check bugzilla.

If it's a known bug or a design choice (i.e. you find an invalid or wontfix bug on your issue), you'll know that for sure, no need to guess. You you may even find a workaround (or post yours) for your issues in the bug you found.

What's worse, if it's a bug, and nobody files it, assuming it already is filed, the bug has very little chance to be fixed.

QuickSearch - http://www.squarefree.com/bugzilla/quicksearch-help.html - and 'Find Specific bug' in bugzilla are the two easy ways to find the bug you need.
 
Can I also suggest adding disabled="true" and style="[...] colour:black;"? That just gets rid of the text cursor when you hover over the "label".
 
not suitable for RDF examples in your post but for dynamically generated text from JavaScript, use descriptionElm.appendChild(document.createTextNode('some long text that will wrap if space is missing')).

(for localization, simply use string bundles)
 
Thank you for posting this. I was very close to losing my sanity !!
 
Another way of displaying multiline text:
<description>
aaa<html:br/>bbb
</description>
 
You can also dynamically set the text of a description element with white-space: pre; from javascript using
description.textContent="line1\nline2\nline3";
 
Hello,

thanks for this trick but my textboxes don't resize vertically according to the content.
Maybe some constraint imposed by a parent node but i can't find which...
 
An annoying thing with the textbox is that I get a carret when hovering over it and the carret stays in the box when I click it. Doing as Giddie says results in a vertical scroll bar being shown.

with a text node inside it works fine---unless the whole pane is in a tabpanel!!! Then the text won't break again.

It drives me crazy!!!
 
thanks for the textbox, it works
 
Post a Comment



<< Home

This page is powered by Blogger. Isn't yours?