Text Chaining in Draw - Drafting specifications

In this post I will describe some ideas on what we expect from chained text in Draw giving a high-level, pseudo code specification of it.
See my last post here for an overview of the problem.

It was all wrong

In my last post I described some of the necessary changes we wanted to get in the project this year. One of the main problems was that now text is moved only after the user exits from editing mode and the current implementation is only sensitive to edits occurring in the first box in the chain (if you delete content in a box in the middle of a chain, nothing is going to happen in the others).
In that post, however, I don't describe precisely enough what we want from a future implementation.

The expected behavior

The behavior we expect from chaining text in Draw is the same we have in Writer already implemented now: when user edits text (adding text, removing text, changing formatting and font size or whatever) in any of the boxes in the chain the implementation should:

  • restructure the whole text in the chain, i.e. redistribute appropriately the text among the boxes (I will call this process "spreading");
  • position the editing pointer at the right updated spot (e.g. if the user pasted some text overflowing in a next box the pointer should be in the latter at the end of the pasted text).

A high-level functional view of spreading

Currently I have no clue of how the mechanism above should be implemented in editeng, svx and the other modules responsible for text dynamics in LO Draw. If we wanted to idealize the process and LO internals, I would now like to propose the "only" two components we may need to achieve the result above. Notice that, for the time being, we want to understand the issue of what before even touching the problem of how, so I will make little or no consideration of efficiency or compatibility with existing architecture whatsoever. The description is in some kind of statically typed pseudolanguage, none of the data types I use refer to real data types in LO code.

The Spreader

Suppose the we have some text T0 already distributed, err... spread, among boxes in a logical chain of text boxes [b_1, b_2..., b_n]. Suppose now that the user edits it (e.g. pasting some content in the middle of it) and we get a new text T through the chain; consider T as the concatenation of the content of all the boxes. Assume that text objects T0 and T contain also information about formatting of the text, the box space it requires for fitting, paragraph breaks, etc... At the same time assume that each box object b_i contains info on their respective sizes.

Vector<Texts> spreadText(TextBoxChain Bxs, WholeText T)
{
  /*
  Given in input chain of boxes Bxs = [b_1, b_2,...b_n] and text T
  should return a vector of texts [t_1, t_2,... t_n] such that
  1) t_1, t_2 ...t_n give T if "concatenated" together
  2) each t_i fits inside box b_i
  */
}

I hope the intuition behind the function spreadText is clear enough. The function that spreads the text does what you expect from it: assigns portions of the text to some box so that all fit within the box (let us assume that there is enough space for the whole content for now). Ideally, such a function could be used in the middle of the formatting loop somwehere in ImpEditEngine:

... // new editing has occurred yielding text T

newTexts = spreadText(Bxs, T)
// Replace T with newTexts
...

It is an open question how plausible it is the code we'll write will actually look like that.

Moving the pointer

After spreading the text among the boxes we need to figure out where the editing pointer is now located. For example, suppose the pointer is at the bottom of the first box - say the tenth word in the paragraph - and the user now copies and paste two more words inside it making the text overflow. From a global perspective, the pointer is at the end of the pasted text (after the twelveth word) but where we want the pointer now is in the second box at the end of the first (or second, depending on where overflow occurs) box. Thus we need to be able to map from a "global" position to a "local" position in a specific box and one function like the one below can come handy. (Note for the attentive reader: as far as I remember, in editeng positions are not measured in "words", so take that just for sake of example)

Pair<TextBox, TextPos> global2localPos(TextBoxChain Bxs, WholeText T, TextPos globPos)
{
  /*
  Return a pair (b_j, locPos) such that if
  [t_1, t_2,... t_n] = spreadText([b_1, b_2,...b_n], T)
  then globPos in T corresponds to locPos in t_j.
  */
}

One building block from the other

It seems intuitive to me that the spreading function as described above can be implemented (inefficiently) using global2localPos:

Vector<Texts> spreadText(TextBoxChain Bxs, WholeText T)
{
  foreach word w in T:
        posOfWrd = getStartPos(w,T)
        (bx4w, _) = global2localPos(Bxs, T, posOfWrd) // Haskell-like pattern syntax (we ignore _)
    // Assign w to bx4w
}

So are all problems solved?

Obviously not. There are still a million questions to be answered (and as many to be asked), among which:

  • what would "WholeText" and "Text" look like in editeng? We need to work at the level of words but many classes in editeng might work at the level of paragraphs.
  • as a related question to the previous one: Can we break and recombine paragraph objects easily?
  • Do we have all the information on sizes of text and boxes readily accessible somewhere in the code?