We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Splitter doesn't respect max_size upon resize (it doesn't resize proportionally)
When you have a Splitter in e.g. a boxlayout with some maximum size that depends on another widget's size, max_size is not respected. Also, I'd be nice, if when resizing the parent, the splitter's size adjusts itself proportionally to what it was before.

To see the issue, run the following code, maximize the window, move the splitter all the way to the left, and resize the window to a smaller size. The splitter will now be larger than the specified max_size:

```
from kivy.base import runTouchApp
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder


kv = '''
<Test>:
    Label:
        text: 'left'
    Splitter:
        max_size: self.parent.width - 200
        Label:
            text: 'right'


'''
Builder.load_string(kv)

class Test(BoxLayout):
    pass

if __name__ == '__main__':
    runTouchApp(Test())
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `kivy/uix/splitter.py`
Content:
```
1 '''
2 Splitter
3 ======
4 
5 .. versionadded:: 1.5.0
6 
7 .. image:: images/splitter.jpg
8     :align: right
9 
10 The :class:`Splitter` is a widget that helps you re-size it's child
11 widget/layout by letting you re-size it via
12 dragging the boundary or double tapping the boundary. This widget is similar to
13 the :class:`~kivy.uix.scrollview.ScrollView` in that it allows only one child
14 widget.
15 
16 Usage::
17 
18     splitter = Splitter(sizable_from = 'right')
19     splitter.add_widget(layout_or_widget_instance)
20     splitter.min_size = 100
21     splitter.max_size = 250
22 
23 To change the size of the strip/border used for resizing::
24 
25     splitter.strip_size = '10pt'
26 
27 To change it's appearance::
28 
29     splitter.strip_cls = your_custom_class
30 
31 You can also change the appearance of the `strip_cls`, which defaults to
32 :class:`SplitterStrip`, by overriding the `kv` rule in your app::
33 
34     <SplitterStrip>:
35         horizontal: True if self.parent and self.parent.sizable_from[0] \
36 in ('t', 'b') else False
37         background_normal: 'path to normal horizontal image' \
38 if self.horizontal else 'path to vertical normal image'
39         background_down: 'path to pressed horizontal image' \
40 if self.horizontal else 'path to vertical pressed image'
41 
42 '''
43 
44 
45 __all__ = ('Splitter', )
46 
47 from kivy.compat import string_types
48 from kivy.factory import Factory
49 from kivy.uix.button import Button
50 from kivy.properties import (OptionProperty, NumericProperty, ObjectProperty,
51                              ListProperty)
52 from kivy.uix.boxlayout import BoxLayout
53 
54 
55 class SplitterStrip(Button):
56     '''Class used for tbe graphical representation of a
57     :class:`kivy.uix.splitter.SplitterStripe`.
58     '''
59     pass
60 
61 
62 class Splitter(BoxLayout):
63     '''See module documentation.
64 
65     :Events:
66         `on_press`:
67             Fired when the splitter is pressed.
68         `on_release`:
69             Fired when the splitter is released.
70 
71     .. versionchanged:: 1.6.0
72         Added `on_press` and `on_release` events.
73 
74     '''
75 
76     border = ListProperty([4, 4, 4, 4])
77     '''Border used for the
78     :class:`~kivy.graphics.vertex_instructions.BorderImage`
79     graphics instruction.
80 
81     This must be a list of four values: (top, right, bottom, left).
82     Read the BorderImage instructions for more information about how
83     to use it.
84 
85     :attr:`border` is a :class:`~kivy.properties.ListProperty` and
86     defaults to (4, 4, 4, 4).
87     '''
88 
89     strip_cls = ObjectProperty(SplitterStrip)
90     '''Specifies the class of the resize Strip.
91 
92     :attr:`strip_cls` is an :class:`kivy.properties.ObjectProperty` and
93     defaults to :class:`~kivy.uix.splitter.SplitterStrip`, which is of type
94     :class:`~kivy.uix.button.Button`.
95 
96     .. versionchanged:: 1.8.0
97 
98         If you set a string, the :class:`~kivy.factory.Factory` will be used to
99         resolve the class.
100 
101     '''
102 
103     sizable_from = OptionProperty('left', options=(
104         'left', 'right', 'top', 'bottom'))
105     '''Specifies whether the widget is resizable. Options are::
106         `left`, `right`, `top` or `bottom`
107 
108     :attr:`sizable_from` is an :class:`~kivy.properties.OptionProperty`
109     and defaults to `left`.
110     '''
111 
112     strip_size = NumericProperty('10pt')
113     '''Specifies the size of resize strip
114 
115     :attr:`strp_size` is a :class:`~kivy.properties.NumericProperty`
116     defaults to `10pt`
117     '''
118 
119     min_size = NumericProperty('100pt')
120     '''Specifies the minimum size beyond which the widget is not resizable.
121 
122     :attr:`min_size` is a :class:`~kivy.properties.NumericProperty` and
123     defaults to `100pt`.
124     '''
125 
126     max_size = NumericProperty('500pt')
127     '''Specifies the maximum size beyond which the widget is not resizable.
128 
129     :attr:`max_size` is a :class:`~kivy.properties.NumericProperty`
130     and defaults to `500pt`.
131     '''
132 
133     __events__ = ('on_press', 'on_release')
134 
135     def __init__(self, **kwargs):
136         self._container = None
137         self._strip = None
138         super(Splitter, self).__init__(**kwargs)
139 
140     def on_sizable_from(self, instance, sizable_from):
141         if not instance._container:
142             return
143 
144         sup = super(Splitter, instance)
145         _strp = instance._strip
146         if _strp:
147             # remove any previous binds
148             _strp.unbind(on_touch_down=instance.strip_down)
149             _strp.unbind(on_touch_move=instance.strip_move)
150             _strp.unbind(on_touch_up=instance.strip_up)
151             self.unbind(disabled=_strp.setter('disabled'))
152 
153             sup.remove_widget(instance._strip)
154         else:
155             cls = instance.strip_cls
156             if isinstance(cls, string_types):
157                 cls = Factory.get(cls)
158             instance._strip = _strp = cls()
159 
160         sz_frm = instance.sizable_from[0]
161         if sz_frm in ('l', 'r'):
162             _strp.size_hint = None, 1
163             _strp.width = instance.strip_size
164             instance.orientation = 'horizontal'
165             instance.unbind(strip_size=_strp.setter('width'))
166             instance.bind(strip_size=_strp.setter('width'))
167         else:
168             _strp.size_hint = 1, None
169             _strp.height = instance.strip_size
170             instance.orientation = 'vertical'
171             instance.unbind(strip_size=_strp.setter('height'))
172             instance.bind(strip_size=_strp.setter('height'))
173 
174         index = 1
175         if sz_frm in ('r', 'b'):
176             index = 0
177         sup.add_widget(_strp, index)
178 
179         _strp.bind(on_touch_down=instance.strip_down)
180         _strp.bind(on_touch_move=instance.strip_move)
181         _strp.bind(on_touch_up=instance.strip_up)
182         _strp.disabled = self.disabled
183         self.bind(disabled=_strp.setter('disabled'))
184 
185     def add_widget(self, widget, index=0):
186         if self._container or not widget:
187             return Exception('Splitter accepts only one Child')
188         self._container = widget
189         sz_frm = self.sizable_from[0]
190         if sz_frm in ('l', 'r'):
191             widget.size_hint_x = 1
192         else:
193             widget.size_hint_y = 1
194 
195         index = 0
196         if sz_frm in ('r', 'b'):
197             index = 1
198         super(Splitter, self).add_widget(widget, index)
199         self.on_sizable_from(self, self.sizable_from)
200 
201     def remove_widget(self, widget, *largs):
202         super(Splitter, self).remove_widget(widget)
203         if widget == self._container:
204             self._container = None
205 
206     def clear_widgets(self):
207         self.remove_widget(self._container)
208 
209     def strip_down(self, instance, touch):
210         if not instance.collide_point(*touch.pos):
211             return False
212         touch.grab(self)
213         self.dispatch('on_press')
214 
215     def on_press(self):
216         pass
217 
218     def strip_move(self, instance, touch):
219         if touch.grab_current is not instance:
220             return False
221         sign = 1
222         max_size = self.max_size
223         min_size = self.min_size
224         sz_frm = self.sizable_from[0]
225 
226         if sz_frm in ('t', 'b'):
227             diff_y = (touch.dy)
228             if sz_frm == 'b':
229                 sign = -1
230             if self.size_hint_y:
231                 self.size_hint_y = None
232             if self.height > 0:
233                 self.height += sign * diff_y
234             else:
235                 self.height = 1
236 
237             height = self.height
238             self.height = max(min_size, min(height, max_size))
239         else:
240             diff_x = (touch.dx)
241             if sz_frm == 'l':
242                 sign = -1
243             if self.size_hint_x:
244                 self.size_hint_x = None
245             if self.width > 0:
246                 self.width += sign * (diff_x)
247             else:
248                 self.width = 1
249 
250             width = self.width
251             self.width = max(min_size, min(width, max_size))
252 
253     def strip_up(self, instance, touch):
254         if touch.grab_current is not instance:
255             return
256 
257         if touch.is_double_tap:
258             max_size = self.max_size
259             min_size = self.min_size
260             sz_frm = self.sizable_from[0]
261             s = self.size
262 
263             if sz_frm in ('t', 'b'):
264                 if self.size_hint_y:
265                     self.size_hint_y = None
266                 if s[1] - min_size <= max_size - s[1]:
267                     self.height = max_size
268                 else:
269                     self.height = min_size
270             else:
271                 if self.size_hint_x:
272                     self.size_hint_x = None
273                 if s[0] - min_size <= max_size - s[0]:
274                     self.width = max_size
275                 else:
276                     self.width = min_size
277         touch.ungrab(instance)
278         self.dispatch('on_release')
279 
280     def on_release(self):
281         pass
282 
283 
284 if __name__ == '__main__':
285     from kivy.app import App
286     from kivy.uix.button import Button
287     from kivy.uix.floatlayout import FloatLayout
288 
289     class SplitterApp(App):
290 
291         def build(self):
292             root = FloatLayout()
293             bx = BoxLayout()
294             bx.add_widget(Button())
295             bx.add_widget(Button())
296             bx2 = BoxLayout()
297             bx2.add_widget(Button())
298             bx2.add_widget(Button())
299             bx2.add_widget(Button())
300             spl = Splitter(
301                 size_hint=(1, .25),
302                 pos_hint = {'top': 1},
303                 sizable_from = 'bottom')
304             spl1 = Splitter(
305                 sizable_from='left',
306                 size_hint=(None, 1), width=90)
307             spl1.add_widget(Button())
308             bx.add_widget(spl1)
309             spl.add_widget(bx)
310 
311             spl2 = Splitter(size_hint=(.25, 1))
312             spl2.add_widget(bx2)
313             spl2.sizable_from = 'right'
314             root.add_widget(spl)
315             root.add_widget(spl2)
316             return root
317 
318     SplitterApp().run()
319
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/kivy/uix/splitter.py b/kivy/uix/splitter.py
--- a/kivy/uix/splitter.py
+++ b/kivy/uix/splitter.py
@@ -136,6 +136,9 @@
         self._container = None
         self._strip = None
         super(Splitter, self).__init__(**kwargs)
+        self.bind(size=self._do_size,
+                  max_size=self._do_size,
+                  min_size=self._do_size)
 
     def on_sizable_from(self, instance, sizable_from):
         if not instance._container:
@@ -215,6 +218,12 @@
     def on_press(self):
         pass
 
+    def _do_size(self, instance, value):
+        if self.sizable_from[0] in ('l', 'r'):
+            self.width = max(self.min_size, min(self.width, self.max_size))
+        else:
+            self.height = max(self.min_size, min(self.height, self.max_size))
+
     def strip_move(self, instance, touch):
         if touch.grab_current is not instance:
             return False

{"golden_diff": "diff --git a/kivy/uix/splitter.py b/kivy/uix/splitter.py\n--- a/kivy/uix/splitter.py\n+++ b/kivy/uix/splitter.py\n@@ -136,6 +136,9 @@\n         self._container = None\n         self._strip = None\n         super(Splitter, self).__init__(**kwargs)\n+        self.bind(size=self._do_size,\n+                  max_size=self._do_size,\n+                  min_size=self._do_size)\n \n     def on_sizable_from(self, instance, sizable_from):\n         if not instance._container:\n@@ -215,6 +218,12 @@\n     def on_press(self):\n         pass\n \n+    def _do_size(self, instance, value):\n+        if self.sizable_from[0] in ('l', 'r'):\n+            self.width = max(self.min_size, min(self.width, self.max_size))\n+        else:\n+            self.height = max(self.min_size, min(self.height, self.max_size))\n+\n     def strip_move(self, instance, touch):\n         if touch.grab_current is not instance:\n             return False\n", "issue": "Splitter doesn't respect max_size upon resize (it doesn't resize proportionally)\nWhen you have a Splitter in e.g. a boxlayout with some maximum size that depends on another widget's size, max_size is not respected. Also, I'd be nice, if when resizing the parent, the splitter's size adjusts itself proportionally to what it was before.\n\nTo see the issue, run the following code, maximize the window, move the splitter all the way to the left, and resize the window to a smaller size. The splitter will now be larger than the specified max_size:\n\n```\nfrom kivy.base import runTouchApp\nfrom kivy.uix.boxlayout import BoxLayout\nfrom kivy.lang import Builder\n\n\nkv = '''\n<Test>:\n    Label:\n        text: 'left'\n    Splitter:\n        max_size: self.parent.width - 200\n        Label:\n            text: 'right'\n\n\n'''\nBuilder.load_string(kv)\n\nclass Test(BoxLayout):\n    pass\n\nif __name__ == '__main__':\n    runTouchApp(Test())\n```\n\n", "before_files": [{"content": "'''\nSplitter\n======\n\n.. versionadded:: 1.5.0\n\n.. image:: images/splitter.jpg\n    :align: right\n\nThe :class:`Splitter` is a widget that helps you re-size it's child\nwidget/layout by letting you re-size it via\ndragging the boundary or double tapping the boundary. This widget is similar to\nthe :class:`~kivy.uix.scrollview.ScrollView` in that it allows only one child\nwidget.\n\nUsage::\n\n    splitter = Splitter(sizable_from = 'right')\n    splitter.add_widget(layout_or_widget_instance)\n    splitter.min_size = 100\n    splitter.max_size = 250\n\nTo change the size of the strip/border used for resizing::\n\n    splitter.strip_size = '10pt'\n\nTo change it's appearance::\n\n    splitter.strip_cls = your_custom_class\n\nYou can also change the appearance of the `strip_cls`, which defaults to\n:class:`SplitterStrip`, by overriding the `kv` rule in your app::\n\n    <SplitterStrip>:\n        horizontal: True if self.parent and self.parent.sizable_from[0] \\\nin ('t', 'b') else False\n        background_normal: 'path to normal horizontal image' \\\nif self.horizontal else 'path to vertical normal image'\n        background_down: 'path to pressed horizontal image' \\\nif self.horizontal else 'path to vertical pressed image'\n\n'''\n\n\n__all__ = ('Splitter', )\n\nfrom kivy.compat import string_types\nfrom kivy.factory import Factory\nfrom kivy.uix.button import Button\nfrom kivy.properties import (OptionProperty, NumericProperty, ObjectProperty,\n                             ListProperty)\nfrom kivy.uix.boxlayout import BoxLayout\n\n\nclass SplitterStrip(Button):\n    '''Class used for tbe graphical representation of a\n    :class:`kivy.uix.splitter.SplitterStripe`.\n    '''\n    pass\n\n\nclass Splitter(BoxLayout):\n    '''See module documentation.\n\n    :Events:\n        `on_press`:\n            Fired when the splitter is pressed.\n        `on_release`:\n            Fired when the splitter is released.\n\n    .. versionchanged:: 1.6.0\n        Added `on_press` and `on_release` events.\n\n    '''\n\n    border = ListProperty([4, 4, 4, 4])\n    '''Border used for the\n    :class:`~kivy.graphics.vertex_instructions.BorderImage`\n    graphics instruction.\n\n    This must be a list of four values: (top, right, bottom, left).\n    Read the BorderImage instructions for more information about how\n    to use it.\n\n    :attr:`border` is a :class:`~kivy.properties.ListProperty` and\n    defaults to (4, 4, 4, 4).\n    '''\n\n    strip_cls = ObjectProperty(SplitterStrip)\n    '''Specifies the class of the resize Strip.\n\n    :attr:`strip_cls` is an :class:`kivy.properties.ObjectProperty` and\n    defaults to :class:`~kivy.uix.splitter.SplitterStrip`, which is of type\n    :class:`~kivy.uix.button.Button`.\n\n    .. versionchanged:: 1.8.0\n\n        If you set a string, the :class:`~kivy.factory.Factory` will be used to\n        resolve the class.\n\n    '''\n\n    sizable_from = OptionProperty('left', options=(\n        'left', 'right', 'top', 'bottom'))\n    '''Specifies whether the widget is resizable. Options are::\n        `left`, `right`, `top` or `bottom`\n\n    :attr:`sizable_from` is an :class:`~kivy.properties.OptionProperty`\n    and defaults to `left`.\n    '''\n\n    strip_size = NumericProperty('10pt')\n    '''Specifies the size of resize strip\n\n    :attr:`strp_size` is a :class:`~kivy.properties.NumericProperty`\n    defaults to `10pt`\n    '''\n\n    min_size = NumericProperty('100pt')\n    '''Specifies the minimum size beyond which the widget is not resizable.\n\n    :attr:`min_size` is a :class:`~kivy.properties.NumericProperty` and\n    defaults to `100pt`.\n    '''\n\n    max_size = NumericProperty('500pt')\n    '''Specifies the maximum size beyond which the widget is not resizable.\n\n    :attr:`max_size` is a :class:`~kivy.properties.NumericProperty`\n    and defaults to `500pt`.\n    '''\n\n    __events__ = ('on_press', 'on_release')\n\n    def __init__(self, **kwargs):\n        self._container = None\n        self._strip = None\n        super(Splitter, self).__init__(**kwargs)\n\n    def on_sizable_from(self, instance, sizable_from):\n        if not instance._container:\n            return\n\n        sup = super(Splitter, instance)\n        _strp = instance._strip\n        if _strp:\n            # remove any previous binds\n            _strp.unbind(on_touch_down=instance.strip_down)\n            _strp.unbind(on_touch_move=instance.strip_move)\n            _strp.unbind(on_touch_up=instance.strip_up)\n            self.unbind(disabled=_strp.setter('disabled'))\n\n            sup.remove_widget(instance._strip)\n        else:\n            cls = instance.strip_cls\n            if isinstance(cls, string_types):\n                cls = Factory.get(cls)\n            instance._strip = _strp = cls()\n\n        sz_frm = instance.sizable_from[0]\n        if sz_frm in ('l', 'r'):\n            _strp.size_hint = None, 1\n            _strp.width = instance.strip_size\n            instance.orientation = 'horizontal'\n            instance.unbind(strip_size=_strp.setter('width'))\n            instance.bind(strip_size=_strp.setter('width'))\n        else:\n            _strp.size_hint = 1, None\n            _strp.height = instance.strip_size\n            instance.orientation = 'vertical'\n            instance.unbind(strip_size=_strp.setter('height'))\n            instance.bind(strip_size=_strp.setter('height'))\n\n        index = 1\n        if sz_frm in ('r', 'b'):\n            index = 0\n        sup.add_widget(_strp, index)\n\n        _strp.bind(on_touch_down=instance.strip_down)\n        _strp.bind(on_touch_move=instance.strip_move)\n        _strp.bind(on_touch_up=instance.strip_up)\n        _strp.disabled = self.disabled\n        self.bind(disabled=_strp.setter('disabled'))\n\n    def add_widget(self, widget, index=0):\n        if self._container or not widget:\n            return Exception('Splitter accepts only one Child')\n        self._container = widget\n        sz_frm = self.sizable_from[0]\n        if sz_frm in ('l', 'r'):\n            widget.size_hint_x = 1\n        else:\n            widget.size_hint_y = 1\n\n        index = 0\n        if sz_frm in ('r', 'b'):\n            index = 1\n        super(Splitter, self).add_widget(widget, index)\n        self.on_sizable_from(self, self.sizable_from)\n\n    def remove_widget(self, widget, *largs):\n        super(Splitter, self).remove_widget(widget)\n        if widget == self._container:\n            self._container = None\n\n    def clear_widgets(self):\n        self.remove_widget(self._container)\n\n    def strip_down(self, instance, touch):\n        if not instance.collide_point(*touch.pos):\n            return False\n        touch.grab(self)\n        self.dispatch('on_press')\n\n    def on_press(self):\n        pass\n\n    def strip_move(self, instance, touch):\n        if touch.grab_current is not instance:\n            return False\n        sign = 1\n        max_size = self.max_size\n        min_size = self.min_size\n        sz_frm = self.sizable_from[0]\n\n        if sz_frm in ('t', 'b'):\n            diff_y = (touch.dy)\n            if sz_frm == 'b':\n                sign = -1\n            if self.size_hint_y:\n                self.size_hint_y = None\n            if self.height > 0:\n                self.height += sign * diff_y\n            else:\n                self.height = 1\n\n            height = self.height\n            self.height = max(min_size, min(height, max_size))\n        else:\n            diff_x = (touch.dx)\n            if sz_frm == 'l':\n                sign = -1\n            if self.size_hint_x:\n                self.size_hint_x = None\n            if self.width > 0:\n                self.width += sign * (diff_x)\n            else:\n                self.width = 1\n\n            width = self.width\n            self.width = max(min_size, min(width, max_size))\n\n    def strip_up(self, instance, touch):\n        if touch.grab_current is not instance:\n            return\n\n        if touch.is_double_tap:\n            max_size = self.max_size\n            min_size = self.min_size\n            sz_frm = self.sizable_from[0]\n            s = self.size\n\n            if sz_frm in ('t', 'b'):\n                if self.size_hint_y:\n                    self.size_hint_y = None\n                if s[1] - min_size <= max_size - s[1]:\n                    self.height = max_size\n                else:\n                    self.height = min_size\n            else:\n                if self.size_hint_x:\n                    self.size_hint_x = None\n                if s[0] - min_size <= max_size - s[0]:\n                    self.width = max_size\n                else:\n                    self.width = min_size\n        touch.ungrab(instance)\n        self.dispatch('on_release')\n\n    def on_release(self):\n        pass\n\n\nif __name__ == '__main__':\n    from kivy.app import App\n    from kivy.uix.button import Button\n    from kivy.uix.floatlayout import FloatLayout\n\n    class SplitterApp(App):\n\n        def build(self):\n            root = FloatLayout()\n            bx = BoxLayout()\n            bx.add_widget(Button())\n            bx.add_widget(Button())\n            bx2 = BoxLayout()\n            bx2.add_widget(Button())\n            bx2.add_widget(Button())\n            bx2.add_widget(Button())\n            spl = Splitter(\n                size_hint=(1, .25),\n                pos_hint = {'top': 1},\n                sizable_from = 'bottom')\n            spl1 = Splitter(\n                sizable_from='left',\n                size_hint=(None, 1), width=90)\n            spl1.add_widget(Button())\n            bx.add_widget(spl1)\n            spl.add_widget(bx)\n\n            spl2 = Splitter(size_hint=(.25, 1))\n            spl2.add_widget(bx2)\n            spl2.sizable_from = 'right'\n            root.add_widget(spl)\n            root.add_widget(spl2)\n            return root\n\n    SplitterApp().run()\n", "path": "kivy/uix/splitter.py"}], "after_files": [{"content": "'''\nSplitter\n======\n\n.. versionadded:: 1.5.0\n\n.. image:: images/splitter.jpg\n    :align: right\n\nThe :class:`Splitter` is a widget that helps you re-size it's child\nwidget/layout by letting you re-size it via\ndragging the boundary or double tapping the boundary. This widget is similar to\nthe :class:`~kivy.uix.scrollview.ScrollView` in that it allows only one child\nwidget.\n\nUsage::\n\n    splitter = Splitter(sizable_from = 'right')\n    splitter.add_widget(layout_or_widget_instance)\n    splitter.min_size = 100\n    splitter.max_size = 250\n\nTo change the size of the strip/border used for resizing::\n\n    splitter.strip_size = '10pt'\n\nTo change it's appearance::\n\n    splitter.strip_cls = your_custom_class\n\nYou can also change the appearance of the `strip_cls`, which defaults to\n:class:`SplitterStrip`, by overriding the `kv` rule in your app::\n\n    <SplitterStrip>:\n        horizontal: True if self.parent and self.parent.sizable_from[0] \\\nin ('t', 'b') else False\n        background_normal: 'path to normal horizontal image' \\\nif self.horizontal else 'path to vertical normal image'\n        background_down: 'path to pressed horizontal image' \\\nif self.horizontal else 'path to vertical pressed image'\n\n'''\n\n\n__all__ = ('Splitter', )\n\nfrom kivy.compat import string_types\nfrom kivy.factory import Factory\nfrom kivy.uix.button import Button\nfrom kivy.properties import (OptionProperty, NumericProperty, ObjectProperty,\n                             ListProperty)\nfrom kivy.uix.boxlayout import BoxLayout\n\n\nclass SplitterStrip(Button):\n    '''Class used for tbe graphical representation of a\n    :class:`kivy.uix.splitter.SplitterStripe`.\n    '''\n    pass\n\n\nclass Splitter(BoxLayout):\n    '''See module documentation.\n\n    :Events:\n        `on_press`:\n            Fired when the splitter is pressed.\n        `on_release`:\n            Fired when the splitter is released.\n\n    .. versionchanged:: 1.6.0\n        Added `on_press` and `on_release` events.\n\n    '''\n\n    border = ListProperty([4, 4, 4, 4])\n    '''Border used for the\n    :class:`~kivy.graphics.vertex_instructions.BorderImage`\n    graphics instruction.\n\n    This must be a list of four values: (top, right, bottom, left).\n    Read the BorderImage instructions for more information about how\n    to use it.\n\n    :attr:`border` is a :class:`~kivy.properties.ListProperty` and\n    defaults to (4, 4, 4, 4).\n    '''\n\n    strip_cls = ObjectProperty(SplitterStrip)\n    '''Specifies the class of the resize Strip.\n\n    :attr:`strip_cls` is an :class:`kivy.properties.ObjectProperty` and\n    defaults to :class:`~kivy.uix.splitter.SplitterStrip`, which is of type\n    :class:`~kivy.uix.button.Button`.\n\n    .. versionchanged:: 1.8.0\n\n        If you set a string, the :class:`~kivy.factory.Factory` will be used to\n        resolve the class.\n\n    '''\n\n    sizable_from = OptionProperty('left', options=(\n        'left', 'right', 'top', 'bottom'))\n    '''Specifies whether the widget is resizable. Options are::\n        `left`, `right`, `top` or `bottom`\n\n    :attr:`sizable_from` is an :class:`~kivy.properties.OptionProperty`\n    and defaults to `left`.\n    '''\n\n    strip_size = NumericProperty('10pt')\n    '''Specifies the size of resize strip\n\n    :attr:`strp_size` is a :class:`~kivy.properties.NumericProperty`\n    defaults to `10pt`\n    '''\n\n    min_size = NumericProperty('100pt')\n    '''Specifies the minimum size beyond which the widget is not resizable.\n\n    :attr:`min_size` is a :class:`~kivy.properties.NumericProperty` and\n    defaults to `100pt`.\n    '''\n\n    max_size = NumericProperty('500pt')\n    '''Specifies the maximum size beyond which the widget is not resizable.\n\n    :attr:`max_size` is a :class:`~kivy.properties.NumericProperty`\n    and defaults to `500pt`.\n    '''\n\n    __events__ = ('on_press', 'on_release')\n\n    def __init__(self, **kwargs):\n        self._container = None\n        self._strip = None\n        super(Splitter, self).__init__(**kwargs)\n        self.bind(size=self._do_size,\n                  max_size=self._do_size,\n                  min_size=self._do_size)\n\n    def on_sizable_from(self, instance, sizable_from):\n        if not instance._container:\n            return\n\n        sup = super(Splitter, instance)\n        _strp = instance._strip\n        if _strp:\n            # remove any previous binds\n            _strp.unbind(on_touch_down=instance.strip_down)\n            _strp.unbind(on_touch_move=instance.strip_move)\n            _strp.unbind(on_touch_up=instance.strip_up)\n            self.unbind(disabled=_strp.setter('disabled'))\n\n            sup.remove_widget(instance._strip)\n        else:\n            cls = instance.strip_cls\n            if isinstance(cls, string_types):\n                cls = Factory.get(cls)\n            instance._strip = _strp = cls()\n\n        sz_frm = instance.sizable_from[0]\n        if sz_frm in ('l', 'r'):\n            _strp.size_hint = None, 1\n            _strp.width = instance.strip_size\n            instance.orientation = 'horizontal'\n            instance.unbind(strip_size=_strp.setter('width'))\n            instance.bind(strip_size=_strp.setter('width'))\n        else:\n            _strp.size_hint = 1, None\n            _strp.height = instance.strip_size\n            instance.orientation = 'vertical'\n            instance.unbind(strip_size=_strp.setter('height'))\n            instance.bind(strip_size=_strp.setter('height'))\n\n        index = 1\n        if sz_frm in ('r', 'b'):\n            index = 0\n        sup.add_widget(_strp, index)\n\n        _strp.bind(on_touch_down=instance.strip_down)\n        _strp.bind(on_touch_move=instance.strip_move)\n        _strp.bind(on_touch_up=instance.strip_up)\n        _strp.disabled = self.disabled\n        self.bind(disabled=_strp.setter('disabled'))\n\n    def add_widget(self, widget, index=0):\n        if self._container or not widget:\n            return Exception('Splitter accepts only one Child')\n        self._container = widget\n        sz_frm = self.sizable_from[0]\n        if sz_frm in ('l', 'r'):\n            widget.size_hint_x = 1\n        else:\n            widget.size_hint_y = 1\n\n        index = 0\n        if sz_frm in ('r', 'b'):\n            index = 1\n        super(Splitter, self).add_widget(widget, index)\n        self.on_sizable_from(self, self.sizable_from)\n\n    def remove_widget(self, widget, *largs):\n        super(Splitter, self).remove_widget(widget)\n        if widget == self._container:\n            self._container = None\n\n    def clear_widgets(self):\n        self.remove_widget(self._container)\n\n    def strip_down(self, instance, touch):\n        if not instance.collide_point(*touch.pos):\n            return False\n        touch.grab(self)\n        self.dispatch('on_press')\n\n    def on_press(self):\n        pass\n\n    def _do_size(self, instance, value):\n        if self.sizable_from[0] in ('l', 'r'):\n            self.width = max(self.min_size, min(self.width, self.max_size))\n        else:\n            self.height = max(self.min_size, min(self.height, self.max_size))\n\n    def strip_move(self, instance, touch):\n        if touch.grab_current is not instance:\n            return False\n        sign = 1\n        max_size = self.max_size\n        min_size = self.min_size\n        sz_frm = self.sizable_from[0]\n\n        if sz_frm in ('t', 'b'):\n            diff_y = (touch.dy)\n            if sz_frm == 'b':\n                sign = -1\n            if self.size_hint_y:\n                self.size_hint_y = None\n            if self.height > 0:\n                self.height += sign * diff_y\n            else:\n                self.height = 1\n\n            height = self.height\n            self.height = max(min_size, min(height, max_size))\n        else:\n            diff_x = (touch.dx)\n            if sz_frm == 'l':\n                sign = -1\n            if self.size_hint_x:\n                self.size_hint_x = None\n            if self.width > 0:\n                self.width += sign * (diff_x)\n            else:\n                self.width = 1\n\n            width = self.width\n            self.width = max(min_size, min(width, max_size))\n\n    def strip_up(self, instance, touch):\n        if touch.grab_current is not instance:\n            return\n\n        if touch.is_double_tap:\n            max_size = self.max_size\n            min_size = self.min_size\n            sz_frm = self.sizable_from[0]\n            s = self.size\n\n            if sz_frm in ('t', 'b'):\n                if self.size_hint_y:\n                    self.size_hint_y = None\n                if s[1] - min_size <= max_size - s[1]:\n                    self.height = max_size\n                else:\n                    self.height = min_size\n            else:\n                if self.size_hint_x:\n                    self.size_hint_x = None\n                if s[0] - min_size <= max_size - s[0]:\n                    self.width = max_size\n                else:\n                    self.width = min_size\n        touch.ungrab(instance)\n        self.dispatch('on_release')\n\n    def on_release(self):\n        pass\n\n\nif __name__ == '__main__':\n    from kivy.app import App\n    from kivy.uix.button import Button\n    from kivy.uix.floatlayout import FloatLayout\n\n    class SplitterApp(App):\n\n        def build(self):\n            root = FloatLayout()\n            bx = BoxLayout()\n            bx.add_widget(Button())\n            bx.add_widget(Button())\n            bx2 = BoxLayout()\n            bx2.add_widget(Button())\n            bx2.add_widget(Button())\n            bx2.add_widget(Button())\n            spl = Splitter(\n                size_hint=(1, .25),\n                pos_hint = {'top': 1},\n                sizable_from = 'bottom')\n            spl1 = Splitter(\n                sizable_from='left',\n                size_hint=(None, 1), width=90)\n            spl1.add_widget(Button())\n            bx.add_widget(spl1)\n            spl.add_widget(bx)\n\n            spl2 = Splitter(size_hint=(.25, 1))\n            spl2.add_widget(bx2)\n            spl2.sizable_from = 'right'\n            root.add_widget(spl)\n            root.add_widget(spl2)\n            return root\n\n    SplitterApp().run()\n", "path": "kivy/uix/splitter.py"}]}