Repository: beeware/toga

Issue:
Empty Cells inside DetailedList
## Expected Behavior

Show text inside the cells of a DetailedList.

## Current Behavior

The cells are empty.

Output when starting the program described below:

```
Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 232, in 'calling callback function'
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/rubicon/objc/api.py", line 232, in __call__
    result = self.py_method(py_self, *args)
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/toga_cocoa/app.py", line 63, in application_openFiles_
    self.impl.open_document(str(fileURL.absoluteString))
AttributeError: 'App' object has no attribute 'open_document'
Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 232, in 'calling callback function'
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/rubicon/objc/api.py", line 232, in __call__
    result = self.py_method(py_self, *args)
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/toga_cocoa/widgets/internal/cells.py", line 75, in drawInteriorWithFrame_inView_
    title = self.objectValue.attrs['title']
KeyError: 'title'
Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 232, in 'calling callback function'
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/rubicon/objc/api.py", line 232, in __call__
    result = self.py_method(py_self, *args)
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/toga_cocoa/widgets/internal/cells.py", line 75, in drawInteriorWithFrame_inView_
    title = self.objectValue.attrs['title']
KeyError: 'title'
Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 232, in 'calling callback function'
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/rubicon/objc/api.py", line 232, in __call__
    result = self.py_method(py_self, *args)
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/toga_cocoa/widgets/internal/cells.py", line 75, in drawInteriorWithFrame_inView_
    title = self.objectValue.attrs['title']
KeyError: 'title'
Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 232, in 'calling callback function'
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/rubicon/objc/api.py", line 232, in __call__
    result = self.py_method(py_self, *args)
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/toga_cocoa/widgets/internal/cells.py", line 74, in drawInteriorWithFrame_inView_
    icon = self.objectValue.attrs['icon']
KeyError: 'icon'
Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 232, in 'calling callback function'
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/rubicon/objc/api.py", line 232, in __call__
    result = self.py_method(py_self, *args)
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/toga_cocoa/widgets/internal/cells.py", line 74, in drawInteriorWithFrame_inView_
    icon = self.objectValue.attrs['icon']
KeyError: 'icon'
Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 232, in 'calling callback function'
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/rubicon/objc/api.py", line 232, in __call__
    result = self.py_method(py_self, *args)
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/toga_cocoa/widgets/internal/cells.py", line 74, in drawInteriorWithFrame_inView_
    icon = self.objectValue.attrs['icon']
KeyError: 'icon'
Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 232, in 'calling callback function'
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/rubicon/objc/api.py", line 232, in __call__
    result = self.py_method(py_self, *args)
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/toga_cocoa/widgets/internal/cells.py", line 74, in drawInteriorWithFrame_inView_
    icon = self.objectValue.attrs['icon']
KeyError: 'icon'
Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 232, in 'calling callback function'
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/rubicon/objc/api.py", line 232, in __call__
    result = self.py_method(py_self, *args)
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/toga_cocoa/widgets/internal/cells.py", line 74, in drawInteriorWithFrame_inView_
    icon = self.objectValue.attrs['icon']
KeyError: 'icon'
Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 232, in 'calling callback function'
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/rubicon/objc/api.py", line 232, in __call__
    result = self.py_method(py_self, *args)
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/toga_cocoa/widgets/internal/cells.py", line 74, in drawInteriorWithFrame_inView_
    icon = self.objectValue.attrs['icon']
KeyError: 'icon'
Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 232, in 'calling callback function'
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/rubicon/objc/api.py", line 232, in __call__
    result = self.py_method(py_self, *args)
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/toga_cocoa/widgets/internal/cells.py", line 74, in drawInteriorWithFrame_inView_
    icon = self.objectValue.attrs['icon']
KeyError: 'icon'
Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 232, in 'calling callback function'
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/rubicon/objc/api.py", line 232, in __call__
    result = self.py_method(py_self, *args)
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/toga_cocoa/widgets/internal/cells.py", line 74, in drawInteriorWithFrame_inView_
    icon = self.objectValue.attrs['icon']
KeyError: 'icon'
Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 232, in 'calling callback function'
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/rubicon/objc/api.py", line 232, in __call__
    result = self.py_method(py_self, *args)
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/toga_cocoa/widgets/internal/cells.py", line 74, in drawInteriorWithFrame_inView_
    icon = self.objectValue.attrs['icon']
KeyError: 'icon'
Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 232, in 'calling callback function'
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/rubicon/objc/api.py", line 232, in __call__
    result = self.py_method(py_self, *args)
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/toga_cocoa/widgets/internal/cells.py", line 75, in drawInteriorWithFrame_inView_
    title = self.objectValue.attrs['title']
KeyError: 'title'
Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 232, in 'calling callback function'
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/rubicon/objc/api.py", line 232, in __call__
    result = self.py_method(py_self, *args)
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/toga_cocoa/widgets/internal/cells.py", line 75, in drawInteriorWithFrame_inView_
    title = self.objectValue.attrs['title']
KeyError: 'title'
Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 232, in 'calling callback function'
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/rubicon/objc/api.py", line 232, in __call__
    result = self.py_method(py_self, *args)
  File "/Users/jannikbuddeberg/.local/share/virtualenvs/toga-test-ZxzYLZpq/lib/python3.7/site-packages/toga_cocoa/widgets/internal/cells.py", line 75, in drawInteriorWithFrame_inView_
    title = self.objectValue.attrs['title']
KeyError: 'title'
```

## Steps to reproduce
<!--- Provide a set of steps describing how to reproduce this bug. If you have a live example, provide the link below -->
1. `$ pipenv install toga`
2. Put the following in main.py
```python
import toga
from toga.sources import ListSource


class Workout(toga.App):
    def startup(self):
        self.main_window = toga.MainWindow(title=self.name)

        # Select workout view
        self.box = toga.Box()

        test_list = ["test", "lorem", "ipsum"]

        dlist = toga.DetailedList(data=test_list)
        dlist2 = toga.DetailedList(data=ListSource(data=test_list, accessors=["title"]))

        test_list3 = []
        for i in test_list:
            test_list3.append(i)
            test_list3.append("test")
        dlist3 = toga.DetailedList(
            data=ListSource(data=test_list3, accessors=["title", "icon"])
        )
        dlist4 = toga.DetailedList(
            data=["Item 0", "Item 1", "Item 2"], on_select=lambda *x, **y: print(x, y)
        )

        self.box.add(dlist)
        self.box.add(dlist2)
        self.box.add(dlist3)
        self.box.add(dlist4)
        self.main_window.content = self.box
        self.main_window.show()


def main():
    return Workout("Test", "test")


if __name__ == "__main__":
    main().main_loop()
```
3. `$ pipenv shell`
4. `$ python main.py`

## Your Environment
I created it using:
```
$ pipenv install toga
```
`$ pipenv graph` shows the following:
```
toga==0.3.0.dev11
  - toga-cocoa [required: ==0.3.0.dev11, installed: 0.3.0.dev11]
    - rubicon-objc [required: >=0.3.0, installed: 0.3.1]
    - toga-core [required: ==0.3.0.dev11, installed: 0.3.0.dev11]
      - travertino [required: >=0.1.0, installed: 0.1.2]
```


* Python Version (list the specific version number)
Python 3.7.3
* Operating System and Version (select from the following and list the specific version number; if your OS is not listed, list that as well)

  - [x] macOS - version: 10.14.5 (18F132)
  - [ ] Linux - distro:   - version:
  - [ ] Windows - version:
  - [ ] Other - name:  - version:

* Toga Target (the type of app you are trying to generate)
 
  - [ ] android
  - [x] cocoa
  - [ ] django 
  - [ ] gtk
  - [ ] iOS
  - [ ] tvOS
  - [ ] watchOS
  - [ ] winforms 
  - [ ] win32
  - [ ] Other (please specify)



Discussion:
I've got it working by changing the code of my application to:
```python
import toga
from toga.icons import Icon


class Workout(toga.App):
    def startup(self):
        self.main_window = toga.MainWindow(title=self.name)

        # Select workout view
        self.box = toga.Box()

        test_list = [
            (Icon("icons8-test-80.png"), "test", "Lorem ipsum"),
            (Icon("icons8-test-80.png"), "test2", "hi how"),
            (Icon("icons8-test-80.png"), "title", "description"),
        ]

        dlist = toga.DetailedList(data=test_list)

        self.box.add(dlist)
        self.main_window.content = self.box
        self.main_window.show()


def main():
    return Workout("Test", "test")


if __name__ == "__main__":
    main().main_loop()
```

And changing the lines 80-82 of toga_cocoa/widgets/internal/cells.py from
```
        icon = self.objectValue.attrs["icon"]
        title = self.objectValue.attrs["title"]
        subtitle = self.objectValue.attrs["subtitle"]
```
to 
```
        icon = self.objectValue.attrs["icon"]
        title = self.objectValue.attrs["label1"]
        subtitle = self.objectValue.attrs["label2"]
```

Resolving patch:
diff --git a/src/core/toga/widgets/detailedlist.py b/src/core/toga/widgets/detailedlist.py
--- a/src/core/toga/widgets/detailedlist.py
+++ b/src/core/toga/widgets/detailedlist.py
@@ -26,10 +26,20 @@ class DetailedList(Widget):
         >>>
         >>> dlist = toga.DetailedList(data=['Item 0', 'Item 1', 'Item 2'], on_select=selection_handler)
     """
+
     MIN_HEIGHT = 100
     MIN_WIDTH = 100
 
-    def __init__(self, id=None, data=None, on_delete=None, on_refresh=None, on_select=None, style=None, factory=None):
+    def __init__(
+        self,
+        id=None,
+        data=None,
+        on_delete=None,
+        on_refresh=None,
+        on_select=None,
+        style=None,
+        factory=None,
+    ):
         super().__init__(id=id, style=style, factory=factory)
         self._data = None
         self._impl = self.factory.DetailedList(interface=self)
@@ -52,9 +62,9 @@ def data(self):
     @data.setter
     def data(self, data):
         if data is None:
-            self._data = ListSource(data=[], accessors=['icon', 'label1', 'label2'])
+            self._data = ListSource(data=[], accessors=["icon", "title", "subtitle"])
         elif isinstance(data, (list, tuple)):
-            self._data = ListSource(data=data, accessors=['icon', 'label1', 'label2'])
+            self._data = ListSource(data=data, accessors=["icon", "title", "subtitle"])
         else:
             self._data = data
 
@@ -129,4 +139,3 @@ def on_select(self):
     def on_select(self, handler: callable):
         self._on_select = wrapped_handler(self, handler)
         self._impl.set_on_select(self._on_select)
-


Test patch: