Repository: kivy/kivy

Issue:
StackLayout children rearrange themselves unexpectedly when their parent's size changes 
Here is [the code ](https://gist.github.com/JackAnderson5/2c24c2bcf6da4bcee6219f1fabb16014).

I'm trying to populate a StackLayout with 5 buttons, so i set their `size_hint_x` to "one fifth" of their parent, or `1/5.`. This is equal to `0.2`. 

So, when parent width is `800` everything is fine; 5 buttons per row.

When the parent width is `801` each button has width `801*0.2`, that is `160.20000000000002` (floating point precision issues... the actual value should be `160.2`). The problem is that if we add 5 of those we get `160.20000000000002*5` which is `801.0000000000001`, which is greater than `801`. Therefor **i assume** StackLayout says "that exceeds my dimensions! Send it to the next raw."; results in 4 buttons per row.

In other words, floating point precision increases `width` or `height` by a tiny bit making children not fit sometimes. 

<i>Note that `801*(1/5.)` **!=** `801*1/5`, kivy gets the first one.</i>



Discussion:
That seems like a problem that would be hard to solve generally.

Wouldn't simply rounding down the size of children solve it? 

e.g. `160.20000000000002` -> `160.200000000`

Or perhaps subtracting from all children size an insignificant amount:

e.g. `160.20000000000002 - .000000000001` -> `160.19999999999902`

Yeah, but is a significant amount? What works for your case will not work when the number of children is a lot higher. And rounding down arbitrarily the size of children by one will display incorrectly in other situations. 

I'm not sure if rounding will not effect how the user sees graphics when we display anything that is smaller than one pixel on the same line. Because sometimes it really does need to be displayed on the next row. Although I'm not sure how sensitive users are to these sub-pixel issues.

I'm not very familiar with kivy but.. 

> [...]will not work when the number of children is a lot higher

.. in order to be off by a single pixel in a StackLayout there would have to be trillions\* of widgets on the same row of that StackLayout. 

*assuming we use something like `round(x, 12)` instead of e.g. `round(x, 3)`

Yeah, I guess you are right. Something like when `sum(children) - available_width < 1e-9` they are considered to be on the same line. A PR is welcome.

[Benchmarking](https://gist.github.com/JackAnderson5/ae484a8f49cdda9aa9ecd9627a5e197d) indicates `round` is 15 times slower, so i used the subtraction.

WARNING: (About the PR) I didn't understand at all what StackLayout code does. Its `do_layout` is a 200SLOC method :P (I think it ll need some heavy refactoring in the future). Instead i simply ran the debugger, replicated the resizing and searched where `160.20000000000002` occurred. I founded it, changed relevant line, and tested it. Seems to be working fine.


Resolving patch:
diff --git a/kivy/uix/stacklayout.py b/kivy/uix/stacklayout.py
--- a/kivy/uix/stacklayout.py
+++ b/kivy/uix/stacklayout.py
@@ -152,8 +152,6 @@ def do_layout(self, *largs):
         padding_y = padding_top + padding_bottom
         spacing_x, spacing_y = self.spacing
 
-        lc = []
-
         # Determine which direction and in what order to place the widgets
         posattr = [0] * 2
         posdelta = [0] * 2
@@ -210,6 +208,7 @@ def do_layout(self, *largs):
         vrev = (deltav < 0)
         firstchild = self.children[0]
         sizes = []
+        lc = []
         for c in reversed(self.children):
             if c.size_hint[outerattr]:
                 c.size[outerattr] = max(1,
@@ -245,7 +244,11 @@ def do_layout(self, *largs):
                     testsizes[-1] = max(0, c.size[innerattr])
                 availsize -= testsizes[-1]
 
-            if availsize >= 0 or not lc:
+            # Tiny value added in order to avoid issues with float precision
+            # causing unexpected children reordering when parent resizes.
+            # e.g. if size is 101 and children size_hint_x is 1./5
+            # 5 children would not fit in one line because 101*(1./5) > 101/5
+            if (availsize + 1e-10) >= 0 or not lc:
                 # even if there's no space, we always add one widget to a row
                 lc.append(c)
                 sizes = testsizes


Test patch: