We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
DropDown defined as dynamic class in kv file does not work
Test file and kivy log are here: https://gist.github.com/PARC6502/7e47667cdea5cbd0e6a4

I'm using this branch of kivy, not sure if that's the issue: https://github.com/matham/kivy/tree/focus-text

If I define a custom DropDown widget in the kv file as a dynamic class, I get an error when I try to open it. If I have the exact same setup but the class is defined in python it works.


--- 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/dropdown.py`
Content:
```
1 '''
2 Drop-Down List
3 ==============
4 
5 .. versionadded:: 1.4.0
6 
7 A versatile drop-down list that can be used with custom widgets. It allows you
8 to display a list of widgets under a displayed widget. Unlike other toolkits,
9 the list of widgets can contain any type of widget: simple buttons,
10 images etc.
11 
12 The positioning of the drop-down list is fully automatic: we will always try to
13 place the dropdown list in a way that the user can select an item in the list.
14 
15 Basic example
16 -------------
17 
18 A button with a dropdown list of 10 possible values. All the buttons within the
19 dropdown list will trigger the dropdown :meth:`DropDown.select` method. After
20 being called, the main button text will display the selection of the
21 dropdown. ::
22 
23     from kivy.uix.dropdown import DropDown
24     from kivy.uix.button import Button
25     from kivy.base import runTouchApp
26 
27     # create a dropdown with 10 buttons
28     dropdown = DropDown()
29     for index in range(10):
30         # when adding widgets, we need to specify the height manually (disabling
31         # the size_hint_y) so the dropdown can calculate the area it needs.
32         btn = Button(text='Value %d' % index, size_hint_y=None, height=44)
33 
34         # for each button, attach a callback that will call the select() method
35         # on the dropdown. We'll pass the text of the button as the data of the
36         # selection.
37         btn.bind(on_release=lambda btn: dropdown.select(btn.text))
38 
39         # then add the button inside the dropdown
40         dropdown.add_widget(btn)
41 
42     # create a big main button
43     mainbutton = Button(text='Hello', size_hint=(None, None))
44 
45     # show the dropdown menu when the main button is released
46     # note: all the bind() calls pass the instance of the caller (here, the
47     # mainbutton instance) as the first argument of the callback (here,
48     # dropdown.open.).
49     mainbutton.bind(on_release=dropdown.open)
50 
51     # one last thing, listen for the selection in the dropdown list and
52     # assign the data to the button text.
53     dropdown.bind(on_select=lambda instance, x: setattr(mainbutton, 'text', x))
54 
55     runTouchApp(mainbutton)
56 
57 Extending dropdown in Kv
58 ------------------------
59 
60 You could create a dropdown directly from your kv::
61 
62     #:kivy 1.4.0
63     <CustomDropDown>:
64         Button:
65             text: 'My first Item'
66             size_hint_y: None
67             height: 44
68             on_release: root.select('item1')
69         Label:
70             text: 'Unselectable item'
71             size_hint_y: None
72             height: 44
73         Button:
74             text: 'My second Item'
75             size_hint_y: None
76             height: 44
77             on_release: root.select('item2')
78 
79 And then, create the associated python class and use it::
80 
81     class CustomDropDown(DropDown):
82         pass
83 
84     dropdown = CustomDropDown()
85     mainbutton = Button(text='Hello', size_hint=(None, None))
86     mainbutton.bind(on_release=dropdown.open)
87     dropdown.bind(on_select=lambda instance, x: setattr(mainbutton, 'text', x))
88 '''
89 
90 __all__ = ('DropDown', )
91 
92 from kivy.uix.scrollview import ScrollView
93 from kivy.properties import ObjectProperty, NumericProperty, BooleanProperty
94 from kivy.core.window import Window
95 from kivy.lang import Builder
96 
97 Builder.load_string('''
98 <DropDown>:
99     container: container
100     do_scroll_x: False
101     size_hint: None, None
102 
103     GridLayout:
104         id: container
105         size_hint_y: None
106         height: self.minimum_size[1]
107         cols: 1
108 ''')
109 
110 
111 class DropDownException(Exception):
112     '''DropDownException class.
113     '''
114     pass
115 
116 
117 class DropDown(ScrollView):
118     '''DropDown class. See module documentation for more information.
119 
120     :Events:
121         `on_select`: data
122             Fired when a selection is done. The data of the selection is passed
123             in as the first argument and is what you pass in the :meth:`select`
124             method as the first argument.
125         `on_dismiss`:
126             .. versionadded:: 1.8.0
127 
128             Fired when the DropDown is dismissed, either on selection or on
129             touching outside the widget.
130     '''
131 
132     auto_width = BooleanProperty(True)
133     '''By default, the width of the dropdown will be the same as the width of
134     the attached widget. Set to False if you want to provide your own width.
135     '''
136 
137     max_height = NumericProperty(None, allownone=True)
138     '''Indicate the maximum height that the dropdown can take. If None, it will
139     take the maximum height available until the top or bottom of the screen
140     is reached.
141 
142     :attr:`max_height` is a :class:`~kivy.properties.NumericProperty` and
143     defaults to None.
144     '''
145 
146     dismiss_on_select = BooleanProperty(True)
147     '''By default, the dropdown will be automatically dismissed when a
148     selection has been done. Set to False to prevent the dismiss.
149 
150     :attr:`dismiss_on_select` is a :class:`~kivy.properties.BooleanProperty`
151     and defaults to True.
152     '''
153 
154     auto_dismiss = BooleanProperty(True)
155     '''By default, the dropdown will be automatically dismissed when a
156     touch happens outside of it, this option allow to disable this
157     feature
158 
159     :attr:`auto_dismiss` is a :class:`~kivy.properties.BooleanProperty`
160     and defaults to True.
161 
162     .. versionadded:: 1.8.0
163     '''
164 
165     attach_to = ObjectProperty(allownone=True)
166     '''(internal) Property that will be set to the widget to which the
167     drop down list is attached.
168 
169     The :meth:`open` method will automatically set this property whilst
170     :meth:`dismiss` will set it back to None.
171     '''
172 
173     container = ObjectProperty()
174     '''(internal) Property that will be set to the container of the dropdown
175     list. It is a :class:`~kivy.uix.gridlayout.GridLayout` by default.
176     '''
177 
178     __events__ = ('on_select', 'on_dismiss')
179 
180     def __init__(self, **kwargs):
181         self._win = None
182         super(DropDown, self).__init__(**kwargs)
183         Window.bind(on_key_down=self.on_key_down)
184         self.bind(size=self._reposition)
185 
186     def on_key_down(self, instance, key, scancode, codepoint, modifiers):
187         if key == 27 and self.get_parent_window():
188             self.dismiss()
189             return True
190 
191     def on_container(self, instance, value):
192         self.container.bind(minimum_size=self._container_minimum_size)
193 
194     def open(self, widget):
195         '''Open the dropdown list and attach it to a specific widget.
196         Depending on the position of the widget within the window and
197         the height of the dropdown, the dropdown might be above or below
198         that widget.
199         '''
200         # ensure we are not already attached
201         if self.attach_to is not None:
202             self.dismiss()
203 
204         # we will attach ourself to the main window, so ensure the
205         # widget we are looking for have a window
206         self._win = widget.get_parent_window()
207         if self._win is None:
208             raise DropDownException(
209                 'Cannot open a dropdown list on a hidden widget')
210 
211         self.attach_to = widget
212         widget.bind(pos=self._reposition, size=self._reposition)
213         self._reposition()
214 
215         # attach ourself to the main window
216         self._win.add_widget(self)
217 
218     def dismiss(self, *largs):
219         '''Remove the dropdown widget from the window and detach it from
220         the attached widget.
221         '''
222         if self.parent:
223             self.parent.remove_widget(self)
224         if self.attach_to:
225             self.attach_to.unbind(pos=self._reposition, size=self._reposition)
226             self.attach_to = None
227         self.dispatch('on_dismiss')
228 
229     def on_dismiss(self):
230         pass
231 
232     def select(self, data):
233         '''Call this method to trigger the `on_select` event with the `data`
234         selection. The `data` can be anything you want.
235         '''
236         self.dispatch('on_select', data)
237         if self.dismiss_on_select:
238             self.dismiss()
239 
240     def on_select(self, data):
241         pass
242 
243     def _container_minimum_size(self, instance, size):
244         if self.max_height:
245             self.height = min(size[1], self.max_height)
246             self.do_scroll_y = size[1] > self.max_height
247         else:
248             self.height = size[1]
249             self.do_scroll_y = True
250 
251     def add_widget(self, *largs):
252         if self.container:
253             return self.container.add_widget(*largs)
254         return super(DropDown, self).add_widget(*largs)
255 
256     def remove_widget(self, *largs):
257         if self.container:
258             return self.container.remove_widget(*largs)
259         return super(DropDown, self).remove_widget(*largs)
260 
261     def clear_widgets(self):
262         if self.container:
263             return self.container.clear_widgets()
264         return super(DropDown, self).clear_widgets()
265 
266     def on_touch_down(self, touch):
267         if super(DropDown, self).on_touch_down(touch):
268             return True
269         if self.collide_point(*touch.pos):
270             return True
271         if self.attach_to and self.attach_to.collide_point(*touch.pos):
272             return True
273         if self.auto_dismiss:
274             self.dismiss()
275 
276     def on_touch_up(self, touch):
277         if super(DropDown, self).on_touch_up(touch):
278             return True
279         if 'button' in touch.profile and touch.button.startswith('scroll'):
280             return
281         if self.auto_dismiss:
282             self.dismiss()
283 
284     def _reposition(self, *largs):
285         # calculate the coordinate of the attached widget in the window
286         # coordinate system
287         win = self._win
288         widget = self.attach_to
289         if not widget or not win:
290             return
291         wx, wy = widget.to_window(*widget.pos)
292         wright, wtop = widget.to_window(widget.right, widget.top)
293 
294         # set width and x
295         if self.auto_width:
296             self.width = wright - wx
297 
298         # ensure the dropdown list doesn't get out on the X axis, with a
299         # preference to 0 in case the list is too wide.
300         x = wx
301         if x + self.width > win.width:
302             x = win.width - self.width
303         if x < 0:
304             x = 0
305         self.x = x
306 
307         # determine if we display the dropdown upper or lower to the widget
308         h_bottom = wy - self.height
309         h_top = win.height - (wtop + self.height)
310         if h_bottom > 0:
311             self.top = wy
312         elif h_top > 0:
313             self.y = wtop
314         else:
315             # none of both top/bottom have enough place to display the
316             # widget at the current size. Take the best side, and fit to
317             # it.
318             height = max(h_bottom, h_top)
319             if height == h_bottom:
320                 self.top = wy
321                 self.height = wy
322             else:
323                 self.y = wtop
324                 self.height = win.height - wtop
325 
326 
327 if __name__ == '__main__':
328     from kivy.uix.button import Button
329     from kivy.base import runTouchApp
330 
331     def show_dropdown(button, *largs):
332         dp = DropDown()
333         dp.bind(on_select=lambda instance, x: setattr(button, 'text', x))
334         for i in range(10):
335             item = Button(text='hello %d' % i, size_hint_y=None, height=44)
336             item.bind(on_release=lambda btn: dp.select(btn.text))
337             dp.add_widget(item)
338         dp.open(button)
339 
340     def touch_move(instance, touch):
341         instance.center = touch.pos
342 
343     btn = Button(text='SHOW', size_hint=(None, None), pos=(300, 200))
344     btn.bind(on_release=show_dropdown, on_touch_move=touch_move)
345 
346     runTouchApp(btn)
347
```
--- 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/dropdown.py b/kivy/uix/dropdown.py
--- a/kivy/uix/dropdown.py
+++ b/kivy/uix/dropdown.py
@@ -94,18 +94,12 @@
 from kivy.core.window import Window
 from kivy.lang import Builder
 
-Builder.load_string('''
-<DropDown>:
-    container: container
-    do_scroll_x: False
-    size_hint: None, None
-
-    GridLayout:
-        id: container
-        size_hint_y: None
-        height: self.minimum_size[1]
-        cols: 1
-''')
+_grid_kv = '''
+GridLayout:
+    size_hint_y: None
+    height: self.minimum_size[1]
+    cols: 1
+'''
 
 
 class DropDownException(Exception):
@@ -179,7 +173,18 @@
 
     def __init__(self, **kwargs):
         self._win = None
+        if 'container' not in kwargs:
+            c = self.container = Builder.load_string(_grid_kv)
+        else:
+            c = None
+        kwargs.setdefault('do_scroll_x', False)
+        if 'size_hint' not in kwargs:
+            kwargs.setdefault('size_hint_x', None)
+            kwargs.setdefault('size_hint_y', None)
         super(DropDown, self).__init__(**kwargs)
+        if c is not None:
+            super(DropDown, self).add_widget(c)
+            self.on_container(self, c)
         Window.bind(on_key_down=self.on_key_down)
         self.bind(size=self._reposition)
 
@@ -189,7 +194,8 @@
             return True
 
     def on_container(self, instance, value):
-        self.container.bind(minimum_size=self._container_minimum_size)
+        if value is not None:
+            self.container.bind(minimum_size=self._container_minimum_size)
 
     def open(self, widget):
         '''Open the dropdown list and attach it to a specific widget.

{"golden_diff": "diff --git a/kivy/uix/dropdown.py b/kivy/uix/dropdown.py\n--- a/kivy/uix/dropdown.py\n+++ b/kivy/uix/dropdown.py\n@@ -94,18 +94,12 @@\n from kivy.core.window import Window\n from kivy.lang import Builder\n \n-Builder.load_string('''\n-<DropDown>:\n-    container: container\n-    do_scroll_x: False\n-    size_hint: None, None\n-\n-    GridLayout:\n-        id: container\n-        size_hint_y: None\n-        height: self.minimum_size[1]\n-        cols: 1\n-''')\n+_grid_kv = '''\n+GridLayout:\n+    size_hint_y: None\n+    height: self.minimum_size[1]\n+    cols: 1\n+'''\n \n \n class DropDownException(Exception):\n@@ -179,7 +173,18 @@\n \n     def __init__(self, **kwargs):\n         self._win = None\n+        if 'container' not in kwargs:\n+            c = self.container = Builder.load_string(_grid_kv)\n+        else:\n+            c = None\n+        kwargs.setdefault('do_scroll_x', False)\n+        if 'size_hint' not in kwargs:\n+            kwargs.setdefault('size_hint_x', None)\n+            kwargs.setdefault('size_hint_y', None)\n         super(DropDown, self).__init__(**kwargs)\n+        if c is not None:\n+            super(DropDown, self).add_widget(c)\n+            self.on_container(self, c)\n         Window.bind(on_key_down=self.on_key_down)\n         self.bind(size=self._reposition)\n \n@@ -189,7 +194,8 @@\n             return True\n \n     def on_container(self, instance, value):\n-        self.container.bind(minimum_size=self._container_minimum_size)\n+        if value is not None:\n+            self.container.bind(minimum_size=self._container_minimum_size)\n \n     def open(self, widget):\n         '''Open the dropdown list and attach it to a specific widget.\n", "issue": "DropDown defined as dynamic class in kv file does not work\nTest file and kivy log are here: https://gist.github.com/PARC6502/7e47667cdea5cbd0e6a4\n\nI'm using this branch of kivy, not sure if that's the issue: https://github.com/matham/kivy/tree/focus-text\n\nIf I define a custom DropDown widget in the kv file as a dynamic class, I get an error when I try to open it. If I have the exact same setup but the class is defined in python it works.\n\n", "before_files": [{"content": "'''\nDrop-Down List\n==============\n\n.. versionadded:: 1.4.0\n\nA versatile drop-down list that can be used with custom widgets. It allows you\nto display a list of widgets under a displayed widget. Unlike other toolkits,\nthe list of widgets can contain any type of widget: simple buttons,\nimages etc.\n\nThe positioning of the drop-down list is fully automatic: we will always try to\nplace the dropdown list in a way that the user can select an item in the list.\n\nBasic example\n-------------\n\nA button with a dropdown list of 10 possible values. All the buttons within the\ndropdown list will trigger the dropdown :meth:`DropDown.select` method. After\nbeing called, the main button text will display the selection of the\ndropdown. ::\n\n    from kivy.uix.dropdown import DropDown\n    from kivy.uix.button import Button\n    from kivy.base import runTouchApp\n\n    # create a dropdown with 10 buttons\n    dropdown = DropDown()\n    for index in range(10):\n        # when adding widgets, we need to specify the height manually (disabling\n        # the size_hint_y) so the dropdown can calculate the area it needs.\n        btn = Button(text='Value %d' % index, size_hint_y=None, height=44)\n\n        # for each button, attach a callback that will call the select() method\n        # on the dropdown. We'll pass the text of the button as the data of the\n        # selection.\n        btn.bind(on_release=lambda btn: dropdown.select(btn.text))\n\n        # then add the button inside the dropdown\n        dropdown.add_widget(btn)\n\n    # create a big main button\n    mainbutton = Button(text='Hello', size_hint=(None, None))\n\n    # show the dropdown menu when the main button is released\n    # note: all the bind() calls pass the instance of the caller (here, the\n    # mainbutton instance) as the first argument of the callback (here,\n    # dropdown.open.).\n    mainbutton.bind(on_release=dropdown.open)\n\n    # one last thing, listen for the selection in the dropdown list and\n    # assign the data to the button text.\n    dropdown.bind(on_select=lambda instance, x: setattr(mainbutton, 'text', x))\n\n    runTouchApp(mainbutton)\n\nExtending dropdown in Kv\n------------------------\n\nYou could create a dropdown directly from your kv::\n\n    #:kivy 1.4.0\n    <CustomDropDown>:\n        Button:\n            text: 'My first Item'\n            size_hint_y: None\n            height: 44\n            on_release: root.select('item1')\n        Label:\n            text: 'Unselectable item'\n            size_hint_y: None\n            height: 44\n        Button:\n            text: 'My second Item'\n            size_hint_y: None\n            height: 44\n            on_release: root.select('item2')\n\nAnd then, create the associated python class and use it::\n\n    class CustomDropDown(DropDown):\n        pass\n\n    dropdown = CustomDropDown()\n    mainbutton = Button(text='Hello', size_hint=(None, None))\n    mainbutton.bind(on_release=dropdown.open)\n    dropdown.bind(on_select=lambda instance, x: setattr(mainbutton, 'text', x))\n'''\n\n__all__ = ('DropDown', )\n\nfrom kivy.uix.scrollview import ScrollView\nfrom kivy.properties import ObjectProperty, NumericProperty, BooleanProperty\nfrom kivy.core.window import Window\nfrom kivy.lang import Builder\n\nBuilder.load_string('''\n<DropDown>:\n    container: container\n    do_scroll_x: False\n    size_hint: None, None\n\n    GridLayout:\n        id: container\n        size_hint_y: None\n        height: self.minimum_size[1]\n        cols: 1\n''')\n\n\nclass DropDownException(Exception):\n    '''DropDownException class.\n    '''\n    pass\n\n\nclass DropDown(ScrollView):\n    '''DropDown class. See module documentation for more information.\n\n    :Events:\n        `on_select`: data\n            Fired when a selection is done. The data of the selection is passed\n            in as the first argument and is what you pass in the :meth:`select`\n            method as the first argument.\n        `on_dismiss`:\n            .. versionadded:: 1.8.0\n\n            Fired when the DropDown is dismissed, either on selection or on\n            touching outside the widget.\n    '''\n\n    auto_width = BooleanProperty(True)\n    '''By default, the width of the dropdown will be the same as the width of\n    the attached widget. Set to False if you want to provide your own width.\n    '''\n\n    max_height = NumericProperty(None, allownone=True)\n    '''Indicate the maximum height that the dropdown can take. If None, it will\n    take the maximum height available until the top or bottom of the screen\n    is reached.\n\n    :attr:`max_height` is a :class:`~kivy.properties.NumericProperty` and\n    defaults to None.\n    '''\n\n    dismiss_on_select = BooleanProperty(True)\n    '''By default, the dropdown will be automatically dismissed when a\n    selection has been done. Set to False to prevent the dismiss.\n\n    :attr:`dismiss_on_select` is a :class:`~kivy.properties.BooleanProperty`\n    and defaults to True.\n    '''\n\n    auto_dismiss = BooleanProperty(True)\n    '''By default, the dropdown will be automatically dismissed when a\n    touch happens outside of it, this option allow to disable this\n    feature\n\n    :attr:`auto_dismiss` is a :class:`~kivy.properties.BooleanProperty`\n    and defaults to True.\n\n    .. versionadded:: 1.8.0\n    '''\n\n    attach_to = ObjectProperty(allownone=True)\n    '''(internal) Property that will be set to the widget to which the\n    drop down list is attached.\n\n    The :meth:`open` method will automatically set this property whilst\n    :meth:`dismiss` will set it back to None.\n    '''\n\n    container = ObjectProperty()\n    '''(internal) Property that will be set to the container of the dropdown\n    list. It is a :class:`~kivy.uix.gridlayout.GridLayout` by default.\n    '''\n\n    __events__ = ('on_select', 'on_dismiss')\n\n    def __init__(self, **kwargs):\n        self._win = None\n        super(DropDown, self).__init__(**kwargs)\n        Window.bind(on_key_down=self.on_key_down)\n        self.bind(size=self._reposition)\n\n    def on_key_down(self, instance, key, scancode, codepoint, modifiers):\n        if key == 27 and self.get_parent_window():\n            self.dismiss()\n            return True\n\n    def on_container(self, instance, value):\n        self.container.bind(minimum_size=self._container_minimum_size)\n\n    def open(self, widget):\n        '''Open the dropdown list and attach it to a specific widget.\n        Depending on the position of the widget within the window and\n        the height of the dropdown, the dropdown might be above or below\n        that widget.\n        '''\n        # ensure we are not already attached\n        if self.attach_to is not None:\n            self.dismiss()\n\n        # we will attach ourself to the main window, so ensure the\n        # widget we are looking for have a window\n        self._win = widget.get_parent_window()\n        if self._win is None:\n            raise DropDownException(\n                'Cannot open a dropdown list on a hidden widget')\n\n        self.attach_to = widget\n        widget.bind(pos=self._reposition, size=self._reposition)\n        self._reposition()\n\n        # attach ourself to the main window\n        self._win.add_widget(self)\n\n    def dismiss(self, *largs):\n        '''Remove the dropdown widget from the window and detach it from\n        the attached widget.\n        '''\n        if self.parent:\n            self.parent.remove_widget(self)\n        if self.attach_to:\n            self.attach_to.unbind(pos=self._reposition, size=self._reposition)\n            self.attach_to = None\n        self.dispatch('on_dismiss')\n\n    def on_dismiss(self):\n        pass\n\n    def select(self, data):\n        '''Call this method to trigger the `on_select` event with the `data`\n        selection. The `data` can be anything you want.\n        '''\n        self.dispatch('on_select', data)\n        if self.dismiss_on_select:\n            self.dismiss()\n\n    def on_select(self, data):\n        pass\n\n    def _container_minimum_size(self, instance, size):\n        if self.max_height:\n            self.height = min(size[1], self.max_height)\n            self.do_scroll_y = size[1] > self.max_height\n        else:\n            self.height = size[1]\n            self.do_scroll_y = True\n\n    def add_widget(self, *largs):\n        if self.container:\n            return self.container.add_widget(*largs)\n        return super(DropDown, self).add_widget(*largs)\n\n    def remove_widget(self, *largs):\n        if self.container:\n            return self.container.remove_widget(*largs)\n        return super(DropDown, self).remove_widget(*largs)\n\n    def clear_widgets(self):\n        if self.container:\n            return self.container.clear_widgets()\n        return super(DropDown, self).clear_widgets()\n\n    def on_touch_down(self, touch):\n        if super(DropDown, self).on_touch_down(touch):\n            return True\n        if self.collide_point(*touch.pos):\n            return True\n        if self.attach_to and self.attach_to.collide_point(*touch.pos):\n            return True\n        if self.auto_dismiss:\n            self.dismiss()\n\n    def on_touch_up(self, touch):\n        if super(DropDown, self).on_touch_up(touch):\n            return True\n        if 'button' in touch.profile and touch.button.startswith('scroll'):\n            return\n        if self.auto_dismiss:\n            self.dismiss()\n\n    def _reposition(self, *largs):\n        # calculate the coordinate of the attached widget in the window\n        # coordinate system\n        win = self._win\n        widget = self.attach_to\n        if not widget or not win:\n            return\n        wx, wy = widget.to_window(*widget.pos)\n        wright, wtop = widget.to_window(widget.right, widget.top)\n\n        # set width and x\n        if self.auto_width:\n            self.width = wright - wx\n\n        # ensure the dropdown list doesn't get out on the X axis, with a\n        # preference to 0 in case the list is too wide.\n        x = wx\n        if x + self.width > win.width:\n            x = win.width - self.width\n        if x < 0:\n            x = 0\n        self.x = x\n\n        # determine if we display the dropdown upper or lower to the widget\n        h_bottom = wy - self.height\n        h_top = win.height - (wtop + self.height)\n        if h_bottom > 0:\n            self.top = wy\n        elif h_top > 0:\n            self.y = wtop\n        else:\n            # none of both top/bottom have enough place to display the\n            # widget at the current size. Take the best side, and fit to\n            # it.\n            height = max(h_bottom, h_top)\n            if height == h_bottom:\n                self.top = wy\n                self.height = wy\n            else:\n                self.y = wtop\n                self.height = win.height - wtop\n\n\nif __name__ == '__main__':\n    from kivy.uix.button import Button\n    from kivy.base import runTouchApp\n\n    def show_dropdown(button, *largs):\n        dp = DropDown()\n        dp.bind(on_select=lambda instance, x: setattr(button, 'text', x))\n        for i in range(10):\n            item = Button(text='hello %d' % i, size_hint_y=None, height=44)\n            item.bind(on_release=lambda btn: dp.select(btn.text))\n            dp.add_widget(item)\n        dp.open(button)\n\n    def touch_move(instance, touch):\n        instance.center = touch.pos\n\n    btn = Button(text='SHOW', size_hint=(None, None), pos=(300, 200))\n    btn.bind(on_release=show_dropdown, on_touch_move=touch_move)\n\n    runTouchApp(btn)\n", "path": "kivy/uix/dropdown.py"}], "after_files": [{"content": "'''\nDrop-Down List\n==============\n\n.. versionadded:: 1.4.0\n\nA versatile drop-down list that can be used with custom widgets. It allows you\nto display a list of widgets under a displayed widget. Unlike other toolkits,\nthe list of widgets can contain any type of widget: simple buttons,\nimages etc.\n\nThe positioning of the drop-down list is fully automatic: we will always try to\nplace the dropdown list in a way that the user can select an item in the list.\n\nBasic example\n-------------\n\nA button with a dropdown list of 10 possible values. All the buttons within the\ndropdown list will trigger the dropdown :meth:`DropDown.select` method. After\nbeing called, the main button text will display the selection of the\ndropdown. ::\n\n    from kivy.uix.dropdown import DropDown\n    from kivy.uix.button import Button\n    from kivy.base import runTouchApp\n\n    # create a dropdown with 10 buttons\n    dropdown = DropDown()\n    for index in range(10):\n        # when adding widgets, we need to specify the height manually (disabling\n        # the size_hint_y) so the dropdown can calculate the area it needs.\n        btn = Button(text='Value %d' % index, size_hint_y=None, height=44)\n\n        # for each button, attach a callback that will call the select() method\n        # on the dropdown. We'll pass the text of the button as the data of the\n        # selection.\n        btn.bind(on_release=lambda btn: dropdown.select(btn.text))\n\n        # then add the button inside the dropdown\n        dropdown.add_widget(btn)\n\n    # create a big main button\n    mainbutton = Button(text='Hello', size_hint=(None, None))\n\n    # show the dropdown menu when the main button is released\n    # note: all the bind() calls pass the instance of the caller (here, the\n    # mainbutton instance) as the first argument of the callback (here,\n    # dropdown.open.).\n    mainbutton.bind(on_release=dropdown.open)\n\n    # one last thing, listen for the selection in the dropdown list and\n    # assign the data to the button text.\n    dropdown.bind(on_select=lambda instance, x: setattr(mainbutton, 'text', x))\n\n    runTouchApp(mainbutton)\n\nExtending dropdown in Kv\n------------------------\n\nYou could create a dropdown directly from your kv::\n\n    #:kivy 1.4.0\n    <CustomDropDown>:\n        Button:\n            text: 'My first Item'\n            size_hint_y: None\n            height: 44\n            on_release: root.select('item1')\n        Label:\n            text: 'Unselectable item'\n            size_hint_y: None\n            height: 44\n        Button:\n            text: 'My second Item'\n            size_hint_y: None\n            height: 44\n            on_release: root.select('item2')\n\nAnd then, create the associated python class and use it::\n\n    class CustomDropDown(DropDown):\n        pass\n\n    dropdown = CustomDropDown()\n    mainbutton = Button(text='Hello', size_hint=(None, None))\n    mainbutton.bind(on_release=dropdown.open)\n    dropdown.bind(on_select=lambda instance, x: setattr(mainbutton, 'text', x))\n'''\n\n__all__ = ('DropDown', )\n\nfrom kivy.uix.scrollview import ScrollView\nfrom kivy.properties import ObjectProperty, NumericProperty, BooleanProperty\nfrom kivy.core.window import Window\nfrom kivy.lang import Builder\n\n_grid_kv = '''\nGridLayout:\n    size_hint_y: None\n    height: self.minimum_size[1]\n    cols: 1\n'''\n\n\nclass DropDownException(Exception):\n    '''DropDownException class.\n    '''\n    pass\n\n\nclass DropDown(ScrollView):\n    '''DropDown class. See module documentation for more information.\n\n    :Events:\n        `on_select`: data\n            Fired when a selection is done. The data of the selection is passed\n            in as the first argument and is what you pass in the :meth:`select`\n            method as the first argument.\n        `on_dismiss`:\n            .. versionadded:: 1.8.0\n\n            Fired when the DropDown is dismissed, either on selection or on\n            touching outside the widget.\n    '''\n\n    auto_width = BooleanProperty(True)\n    '''By default, the width of the dropdown will be the same as the width of\n    the attached widget. Set to False if you want to provide your own width.\n    '''\n\n    max_height = NumericProperty(None, allownone=True)\n    '''Indicate the maximum height that the dropdown can take. If None, it will\n    take the maximum height available until the top or bottom of the screen\n    is reached.\n\n    :attr:`max_height` is a :class:`~kivy.properties.NumericProperty` and\n    defaults to None.\n    '''\n\n    dismiss_on_select = BooleanProperty(True)\n    '''By default, the dropdown will be automatically dismissed when a\n    selection has been done. Set to False to prevent the dismiss.\n\n    :attr:`dismiss_on_select` is a :class:`~kivy.properties.BooleanProperty`\n    and defaults to True.\n    '''\n\n    auto_dismiss = BooleanProperty(True)\n    '''By default, the dropdown will be automatically dismissed when a\n    touch happens outside of it, this option allow to disable this\n    feature\n\n    :attr:`auto_dismiss` is a :class:`~kivy.properties.BooleanProperty`\n    and defaults to True.\n\n    .. versionadded:: 1.8.0\n    '''\n\n    attach_to = ObjectProperty(allownone=True)\n    '''(internal) Property that will be set to the widget to which the\n    drop down list is attached.\n\n    The :meth:`open` method will automatically set this property whilst\n    :meth:`dismiss` will set it back to None.\n    '''\n\n    container = ObjectProperty()\n    '''(internal) Property that will be set to the container of the dropdown\n    list. It is a :class:`~kivy.uix.gridlayout.GridLayout` by default.\n    '''\n\n    __events__ = ('on_select', 'on_dismiss')\n\n    def __init__(self, **kwargs):\n        self._win = None\n        if 'container' not in kwargs:\n            c = self.container = Builder.load_string(_grid_kv)\n        else:\n            c = None\n        kwargs.setdefault('do_scroll_x', False)\n        if 'size_hint' not in kwargs:\n            kwargs.setdefault('size_hint_x', None)\n            kwargs.setdefault('size_hint_y', None)\n        super(DropDown, self).__init__(**kwargs)\n        if c is not None:\n            super(DropDown, self).add_widget(c)\n            self.on_container(self, c)\n        Window.bind(on_key_down=self.on_key_down)\n        self.bind(size=self._reposition)\n\n    def on_key_down(self, instance, key, scancode, codepoint, modifiers):\n        if key == 27 and self.get_parent_window():\n            self.dismiss()\n            return True\n\n    def on_container(self, instance, value):\n        if value is not None:\n            self.container.bind(minimum_size=self._container_minimum_size)\n\n    def open(self, widget):\n        '''Open the dropdown list and attach it to a specific widget.\n        Depending on the position of the widget within the window and\n        the height of the dropdown, the dropdown might be above or below\n        that widget.\n        '''\n        # ensure we are not already attached\n        if self.attach_to is not None:\n            self.dismiss()\n\n        # we will attach ourself to the main window, so ensure the\n        # widget we are looking for have a window\n        self._win = widget.get_parent_window()\n        if self._win is None:\n            raise DropDownException(\n                'Cannot open a dropdown list on a hidden widget')\n\n        self.attach_to = widget\n        widget.bind(pos=self._reposition, size=self._reposition)\n        self._reposition()\n\n        # attach ourself to the main window\n        self._win.add_widget(self)\n\n    def dismiss(self, *largs):\n        '''Remove the dropdown widget from the window and detach it from\n        the attached widget.\n        '''\n        if self.parent:\n            self.parent.remove_widget(self)\n        if self.attach_to:\n            self.attach_to.unbind(pos=self._reposition, size=self._reposition)\n            self.attach_to = None\n        self.dispatch('on_dismiss')\n\n    def on_dismiss(self):\n        pass\n\n    def select(self, data):\n        '''Call this method to trigger the `on_select` event with the `data`\n        selection. The `data` can be anything you want.\n        '''\n        self.dispatch('on_select', data)\n        if self.dismiss_on_select:\n            self.dismiss()\n\n    def on_select(self, data):\n        pass\n\n    def _container_minimum_size(self, instance, size):\n        if self.max_height:\n            self.height = min(size[1], self.max_height)\n            self.do_scroll_y = size[1] > self.max_height\n        else:\n            self.height = size[1]\n            self.do_scroll_y = True\n\n    def add_widget(self, *largs):\n        if self.container:\n            return self.container.add_widget(*largs)\n        return super(DropDown, self).add_widget(*largs)\n\n    def remove_widget(self, *largs):\n        if self.container:\n            return self.container.remove_widget(*largs)\n        return super(DropDown, self).remove_widget(*largs)\n\n    def clear_widgets(self):\n        if self.container:\n            return self.container.clear_widgets()\n        return super(DropDown, self).clear_widgets()\n\n    def on_touch_down(self, touch):\n        if super(DropDown, self).on_touch_down(touch):\n            return True\n        if self.collide_point(*touch.pos):\n            return True\n        if self.attach_to and self.attach_to.collide_point(*touch.pos):\n            return True\n        if self.auto_dismiss:\n            self.dismiss()\n\n    def on_touch_up(self, touch):\n        if super(DropDown, self).on_touch_up(touch):\n            return True\n        if 'button' in touch.profile and touch.button.startswith('scroll'):\n            return\n        if self.auto_dismiss:\n            self.dismiss()\n\n    def _reposition(self, *largs):\n        # calculate the coordinate of the attached widget in the window\n        # coordinate system\n        win = self._win\n        widget = self.attach_to\n        if not widget or not win:\n            return\n        wx, wy = widget.to_window(*widget.pos)\n        wright, wtop = widget.to_window(widget.right, widget.top)\n\n        # set width and x\n        if self.auto_width:\n            self.width = wright - wx\n\n        # ensure the dropdown list doesn't get out on the X axis, with a\n        # preference to 0 in case the list is too wide.\n        x = wx\n        if x + self.width > win.width:\n            x = win.width - self.width\n        if x < 0:\n            x = 0\n        self.x = x\n\n        # determine if we display the dropdown upper or lower to the widget\n        h_bottom = wy - self.height\n        h_top = win.height - (wtop + self.height)\n        if h_bottom > 0:\n            self.top = wy\n        elif h_top > 0:\n            self.y = wtop\n        else:\n            # none of both top/bottom have enough place to display the\n            # widget at the current size. Take the best side, and fit to\n            # it.\n            height = max(h_bottom, h_top)\n            if height == h_bottom:\n                self.top = wy\n                self.height = wy\n            else:\n                self.y = wtop\n                self.height = win.height - wtop\n\n\nif __name__ == '__main__':\n    from kivy.uix.button import Button\n    from kivy.base import runTouchApp\n\n    def show_dropdown(button, *largs):\n        dp = DropDown()\n        dp.bind(on_select=lambda instance, x: setattr(button, 'text', x))\n        for i in range(10):\n            item = Button(text='hello %d' % i, size_hint_y=None, height=44)\n            item.bind(on_release=lambda btn: dp.select(btn.text))\n            dp.add_widget(item)\n        dp.open(button)\n\n    def touch_move(instance, touch):\n        instance.center = touch.pos\n\n    btn = Button(text='SHOW', size_hint=(None, None), pos=(300, 200))\n    btn.bind(on_release=show_dropdown, on_touch_move=touch_move)\n\n    runTouchApp(btn)\n", "path": "kivy/uix/dropdown.py"}]}