shoes/shoes4 — pull request #493 by jasonrclark (2013-12-07 05:30:38+00:00)
Title: Shoes3 compliant TextBlock flowing

:warning: **Very early WIP!** :warning:

I wanted to get this up for review and commentary since it's turning out to be pretty extensive. I'm very eager for comments and suggestions at every level.

The problem with text not flowing like Shoes 3 is well spelled out in issue #373.

Lots to say, but if you're curious to see the current results, see the end of the post :smile:

**The Algorithm**

The basic algorithm is based on what @pragtob suggested. The heart of the algorithm is implemented by `TextBlockFitter#fit_it_in`. This fitting code is used by both slot positioning (to figure where a flowing text block ends), and rendering (to actually write it to the screen).

I pulled the fitting logic into its own class, `TextBlockFitter`. I'm not attached to this separation from existing `TextBlock`-related classes, but it made test-driving and reasoning easier in the meantime.

`Swt::TextLayout` class does the heavy lifting since it knows how to wrap and measure text. We generate different `Swt::TextLayout` objects, see how combination fits, then return that to position or render.

The basic cases are:

* Text fits in the height of the current row with space after it an element can fit into (1 `TextLayout` required)
* Text fits in the height of the current row but takes up the remaining space to the right edge (1 `TextLayout` required)
* Text doesn't fit on the current row, so it flow down below the current elements (2 `TextLayout`s required)

A final third block isn't needed since `Swt::TextLayout` can tell where the text ends to place the next element.

Along with the new fitting algorithm, this required more direct intervention in `Slot`'s current positioning logic. Relying on tinkering with DSL positions ended up being incompatible with a flowed `TextBlock`.

**Work Remaining**
Lots to do still. Here's the high points:

* Lots of issues with calculating positions--vertical space for the first layout, finding the right left value to wrap from, proper placement when the current slot doesn't start at 0,0... I'm suspicious these all still have issues.
* To focus on the core logic, I disabled the styling, links and cursor behavior in `TextBlockPainter`. Those obviously will need to be restored.
* More specs needed around `TextBlockFitter`
* `TextBlock` and `TextBlockPainter` specs are utterly busted in this spike, and will need to be reconciled with the new fitting
* Right now `TextBlock` acts like we're always in a `flow` for positioning the next element, but we could be in a `stack` instead.
* Margins, base text size, and positioning look different from Shoes3. Might not really belong with this PR, but it does introduce differences when comparing how 4 vs 3 flows.

**Results!**
With the following sample code(s) (varying the text length) with the current spike, gets us this:

```
Shoes.app width: 300, height: 300 do
button "A button"
para 'One paragraph ' * 4, :size => 18
button "A button"
end
```

With 1:
![image](https://f.cloud.github.com/assets/130504/1697722/ad1fd966-5efa-11e3-8866-359955772bbf.png)

With 2:
![image](https://f.cloud.github.com/assets/130504/1697718/77c676bc-5efa-11e3-8daa-2f591df574d4.png)

With 3:
![image](https://f.cloud.github.com/assets/130504/1697720/8648215e-5efa-11e3-9db8-7cff37fb0c3c.png)

With 4:
![image](https://f.cloud.github.com/assets/130504/1697721/9728efb2-5efa-11e3-92c3-e7397464089c.png)