Okay so I think there are several things here...
The easiest to deal with is the situation with listBehavior and dontWrap - the fact that listBehavior true iff dontWrap is true is very much legacy from before 5.5. Prior to 5.5 there were various 'optimizations' in place to deal with dontWrap at the MCField level, these were removed when dontWrap became a paragraph property because they no longer made any sense (nor were they making any difference to performance before then). Thus, I can't see an issue with making it so that whilst setting listBehavior true should set dontWrap true (for backwards compatibility), you should be able to turn dontWrap off even if listBehavior is already true (i.e. remove the code in the P_DONT_WRAP setprop in MCField). [ Indeed, you can essentially turn off dontWrap for a list field at the moment - by setting dontWrap false for each paragraph - thus the current forcing of dontWrap to true when setting listBehavior true doesn't make sense any more ].
In regards to the relatively recent processing of VT, I must confess I hadn't considered backward compatibility problems that this might have introduced. So it would be good to know a bit more details as to the problems this change has introduced and whether a compatibility flag is actually required here, or whether it would be better to introduce a field property like 'showNonPrintingChars' (which would render the appropriate symbols for paragraph separators, line separators and tabs, whilst keeping the behavior the control characters intact). This would certainly serve the data display aspect perhaps better than the old behavior (as it would be consistent across platforms, and not be a side-effect of whether the font had the given characters at the correct code points).
In terms of dontWrap turning off VT processing then the current behavior is mainly a side-effect of the architecture of the field - I considered VT to be equivalent to wrapping (it is processed in MCParagraph::flow() and callees which is where word-breaking is done) but this is perhaps not quite correct. So, what situations would you not want VTs to act as explicit line breaks - are there any real-world use-cases?
Putting my 'keeping an eye to the future' hat on and generalizing, I guess what I'm asking is that should line breaking and word wrapping be treated separately, or should they be considered the same thing. Indeed, thinking about the processes you go through when laying out text, in full generality various choices come into play at several points (pretty much none of which you have control over at the moment):
- Text is broken into lines by explicit line breaks to generate explicitly broken lines (explicit breaking)
- The explicitly broken lines are then wrapped by adding implicit line breaks to generate implicitly broken lines (implicit breaking)
- If any implicitly broken lines do not fit in the available space then they are processed again to find a break point within them (fallback implicit breaking)
- The fully broken lines are then fit to the available space (fitting)
- The fully broken lines are then finally aligned to the available space (alignment)
At point (1), there is only a boolean option - whether to ignore explicit line breaks or not (perhaps a ignoreExplicitBreaks property).
At point (2), there are the following potential options (perhaps a wrapMode property):
- none - no implicit breaks (this is dontWrap = true)
- char - break at characters
- hypen - break at hypenation points
- word - break at words (this is dontWrap = false)
At point (3), we are left with lines that are still too long to fit so they can potentially be wrapped again to make them fit with the same options as above (perhaps a fallbackWrapMode property). [ So, for example if the wrapMode were word, and the fallbackWrapMode were char for a word that was too long to fit on a single line, it would be wrapped by adding an implicit break at the appropriate char to make sure it fits - this is the behavior of most word-processors, and the current field behavior is wrapMode = "word", fallbackWrapMode = "none" ].
At point (4), we are left to what to do with overlong lines that cannot be divided by any further (perhaps a fitMode property):
- scroll - the width of the line is the full width of the contents
- clip - the width of the line is the width of the container, and the line is clipped when drawn
- front - keep the front of the text, and replace as much of the back as needed with an ellipsis and clip to the width of the container
- middle - keep as much of the front and back of the text as possible and replace the middle with an ellipsis and clip to the width of the container
- back - keep the back of the text, and replace as much of the front as needed with an ellipsis and clip to the with of the container
At point (5), we are using the 'textAlign' property to position the resulting fragment appropriately relative to the layout width. (Here 'scroll' is essentially the current behavior which I think is actually not very useful as it interacts badly with alignment - you can see this if you have overly long lines in a paragraph what has right or center alignment set - a little more thought is perhaps needed there).
For a point of reference, the choices you are stuck with in the current field are either:
- dontWrap == true - ignoreExplicitBreaks is true, wrapMode is none, fallbackWrapMode is none, fitMode is scroll
- dontWrap == false - ignoreExplicitBreaks is false, wrapMode is word, fallbackWrapMode is none, fitMode is scroll
Okay, so after all that, certainly adding something like an 'ignoreExplicitBreaks' property (at the field and paragraph level) which is independent of dontWrap would fit into the layout process quite logically and succintly without any unpleasant interactions.
So, going back to the implementation aspect of things. Enabling explicit break processing in non-vGrid mode is simple as I think Monte has discovered - 'noflow()' is only called if dontWrap is true and ignoreExplicitBreaks is true, flow() is called in all other cases but if dontWrap is true, then word-wrapping should not take place.
Getting it to work *correctly* in vGrid mode is substantially more difficult. As it turns out I was looking at the nightmare that is the MCLine/MCParagraph interaction the other day when fixing a couple of bugs (and optimizing word wrapping - which is now about twice as fast as it was before). Really the data structure that is MCLine is wrong for what we want to do here. At the moment an MCLine is a linked list of MCBlocks which represent the beginning and end of that implicitly broken line and in order to form this list it has to break the blocks at the implicit break points (which is not only ugly but also really quite inefficient); furthermore doing things like this means that it is not possible for lines to represent text that is out of the order that it is in the field. The latter is essentially what is required in order to do cell layout with wrapping (and explicit breaking) *and* handle right to left text. On top of this, the handling of tabs is essentially being done in entirely the wrong place for table rendering - it is currently done at the block level, whereas that should already have been shaken out in the layout process when generating MCLines. My thinking on the topic had got as far as deciding that MCLine should be a list of cells, which each cell a list of lines, and each line a list of ranges of text that index into the paragraph - but as yet I've not looked at how that impacts rendering, measuring and layout...