From 1e345ac923569bff739ac450aef947bd3d7774ba Mon Sep 17 00:00:00 2001 From: 19766375 <19766375@student.curtin.edu.au> Date: Sun, 11 May 2025 16:38:38 +0800 Subject: [PATCH 1/5] add changelog file --- changes/3334.5.feature.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changes/3334.5.feature.rst diff --git a/changes/3334.5.feature.rst b/changes/3334.5.feature.rst new file mode 100644 index 0000000000..9b538e32c9 --- /dev/null +++ b/changes/3334.5.feature.rst @@ -0,0 +1 @@ +The Web backend now supports Table widgets. From 979333bbfa0ce0b307fef17c5ed02c66b2cabf84 Mon Sep 17 00:00:00 2001 From: 19766375 <19766375@student.curtin.edu.au> Date: Sun, 11 May 2025 16:39:43 +0800 Subject: [PATCH 2/5] add and update table documentation files --- docs/reference/api/widgets/table.rst | 6 ++++-- docs/reference/data/widgets_by_platform.csv | 2 +- docs/reference/images/table-web.png | Bin 0 -> 11973 bytes 3 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 docs/reference/images/table-web.png diff --git a/docs/reference/api/widgets/table.rst b/docs/reference/api/widgets/table.rst index a910c688d9..aafb2d3ce1 100644 --- a/docs/reference/api/widgets/table.rst +++ b/docs/reference/api/widgets/table.rst @@ -34,9 +34,11 @@ necessary. Not supported - .. group-tab:: Web |no| + .. group-tab:: Web - Not supported + .. figure:: /reference/images/table-web.png + :align: center + :width: 450px .. group-tab:: Textual |no| diff --git a/docs/reference/data/widgets_by_platform.csv b/docs/reference/data/widgets_by_platform.csv index 4c7e74cda3..0abd1a59a9 100644 --- a/docs/reference/data/widgets_by_platform.csv +++ b/docs/reference/data/widgets_by_platform.csv @@ -19,7 +19,7 @@ ProgressBar,General Widget,:class:`~toga.ProgressBar`,Progress Bar,|y|,|y|,|y|,| Selection,General Widget,:class:`~toga.Selection`,A widget to select a single option from a list of alternatives.,|y|,|y|,|y|,|y|,|y|,, Slider,General Widget,:class:`~toga.Slider`,Slider,|y|,|y|,|y|,|y|,|y|,, Switch,General Widget,:class:`~toga.Switch`,Switch,|y|,|y|,|y|,|y|,|y|,|b|, -Table,General Widget,:class:`~toga.Table`,A widget for displaying columns of tabular data.,|y|,|y|,|y|,,|b|,, +Table,General Widget,:class:`~toga.Table`,A widget for displaying columns of tabular data.,|y|,|y|,|y|,,|b|,|b|, TextInput,General Widget,:class:`~toga.TextInput`,A widget for the display and editing of a single line of text.,|y|,|y|,|y|,|y|,|y|,|b|,|b| TimeInput,General Widget,:class:`~toga.TimeInput`,A widget to select a clock time,,,|y|,,|y|,, Tree,General Widget,:class:`~toga.Tree`,A widget for displaying a hierarchical tree of tabular data.,|y|,|y|,,,,, diff --git a/docs/reference/images/table-web.png b/docs/reference/images/table-web.png new file mode 100644 index 0000000000000000000000000000000000000000..f0d98e08ffe68784341233407eda3074920be908 GIT binary patch literal 11973 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 S!XHPC0_0^>q{}5uLjDgFW#X3r literal 0 HcmV?d00001 From 1e515a6623b610ca69af4bbeef87e27c20b97009 Mon Sep 17 00:00:00 2001 From: 19766375 <19766375@student.curtin.edu.au> Date: Sun, 11 May 2025 16:40:41 +0800 Subject: [PATCH 3/5] update factory file to add table --- web/src/toga_web/factory.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/web/src/toga_web/factory.py b/web/src/toga_web/factory.py index 3aaa3da4fc..10752ece8b 100644 --- a/web/src/toga_web/factory.py +++ b/web/src/toga_web/factory.py @@ -30,8 +30,7 @@ # from .widgets.slider import Slider # from .widgets.splitcontainer import SplitContainer from .widgets.switch import Switch - -# from .widgets.table import Table +from .widgets.table import Table from .widgets.textinput import TextInput # from .widgets.tree import Tree @@ -76,7 +75,7 @@ def not_implemented(feature): # 'Slider', # 'SplitContainer', "Switch", - # 'Table', + "Table", "TextInput", # 'Tree', # 'WebView', From aaac0a71081c1d5151091b2ed44b10dc52c49223 Mon Sep 17 00:00:00 2001 From: 19766375 <19766375@student.curtin.edu.au> Date: Sun, 11 May 2025 16:42:15 +0800 Subject: [PATCH 4/5] add table widget to web backend --- web/src/toga_web/widgets/table.py | 179 ++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 web/src/toga_web/widgets/table.py diff --git a/web/src/toga_web/widgets/table.py b/web/src/toga_web/widgets/table.py new file mode 100644 index 0000000000..cf11e69eb6 --- /dev/null +++ b/web/src/toga_web/widgets/table.py @@ -0,0 +1,179 @@ +import warnings + +import toga +from toga_web.libs import create_proxy + +from .base import Widget + + +# placeholder from gtk +class TogaRow: + def __init__(self, value): + super().__init__() + self.value = value + + # All paths return none as Icon is not implemented in web. + def icon(self, attr): + data = getattr(self.value, attr, None) + if isinstance(data, tuple): + if data[0] is not None: + return None + return None + else: + try: + return None + except AttributeError: + return None + + def text(self, attr, missing_value): + data = getattr(self.value, attr, None) + + if isinstance(data, toga.Widget): + warnings.warn("Web does not support the use of widgets in cells") + text = None + elif isinstance(data, tuple): + text = data[1] + else: + text = data + + if text is None: + return missing_value + + return str(text) + + +class Table(Widget): + def create(self): + + self.native = self._create_native_widget( + "div", classes=["toga-table-container"] + ) + + self.table = self._create_native_widget( + "table", + ) + self.native.appendChild(self.table) + + self.table_header_group = self._create_native_widget( + "thead", classes=["table-header"] + ) + self.table.appendChild(self.table_header_group) + + self.table_body = self._create_native_widget("tbody", classes=["table-body"]) + self.table.appendChild(self.table_body) + + def change_source(self, source): + self.selection = {} + + # remove old table data + for row_child in list(self.table_body.children): + for td_child in list(row_child.children): + row_child.removeChild(td_child) + self.table_body.removeChild(row_child) + + for row_child in list(self.table_header_group.children): + for td_child in list(row_child.children): + row_child.removeChild(td_child) + self.table_header_group.removeChild(row_child) + + if source is not None: + self._create_table_headers() + + for i, row in enumerate(source): + self._create_table_row(row, i) + + # set table here + self.refresh() + + def get_selection(self): + selection = sorted(self.selection) + if self.interface.multiple_select: + return selection + elif len(selection) == 0: + return None + else: + return selection[0] + + def add_selection(self, index, table_row): + self.selection[index] = table_row + table_row.style.backgroundColor = "lightblue" + # set colour + + def remove_selection(self, index): + table_row = self.selection.pop(index) + table_row.style.backgroundColor = "" + + def clear_selection(self): + for index in list(self.selection): + self.remove_selection(index) + + def _create_table_headers(self): + if self.interface.headings: + headings = self.interface.headings + else: + headings = self.interface.accessors + self.table_header_row = self._create_native_widget( + "tr", classes=["table-header-row"] + ) + + for heading in headings: + th = self._create_native_widget("th", content=heading) + self.table_header_row.appendChild(th) + + self.table_header_group.appendChild(self.table_header_row) + + def _create_table_row(self, item, index): + row = TogaRow(item) + values = [] + for accessor in self.interface.accessors: + values.extend( + [ + # Removed icon accessor for now as not sure how to handle icon + # row.icon(accessor), + row.text(accessor, self.interface.missing_value), + ] + ) + tr = self._create_native_widget( + "tr", + ) + + tr.addEventListener( + "click", create_proxy(lambda event: self.dom_row_click(event, index, tr)) + ) + + for value in values: + td = self._create_native_widget("td", content=value) + tr.appendChild(td) + self.table_body.appendChild(tr) + + def dom_row_click(self, event, index, table_row): + print("row_click listener! row:", index) + if index in self.selection: + self.remove_selection(index) + print("removing row ", index, " from selection") + else: + if not self.interface.multiple_select: + self.clear_selection() + self.add_selection(index, table_row) + print("adding row ", index, " to selection") + + # if self.interface.on_select: + # self.interface.on_select(self.interface) + + def insert(self, index, item): + self.change_source(self.interface.data) + + def clear(self): + self.change_source(self.interface.data) + + def change(self, item): + self.change_source(self.interface.data) + + def remove(self, index, item): + self.change_source(self.interface.data) + + def insert_column(self, index, heading, accessor): + self.change_source(self.interface.data) + + def remove_column(self, accessor): + self.change_source(self.interface.data) From 15c47a7243f71736fc3a0d286145673de6b74fb9 Mon Sep 17 00:00:00 2001 From: 19766375 <19766375@student.curtin.edu.au> Date: Tue, 20 May 2025 10:28:23 +0800 Subject: [PATCH 5/5] Code cleanup --- web/src/toga_web/static/toga.css | 6 ++++ web/src/toga_web/widgets/table.py | 47 ++++++++----------------------- 2 files changed, 18 insertions(+), 35 deletions(-) diff --git a/web/src/toga_web/static/toga.css b/web/src/toga_web/static/toga.css index e3aa84d253..4c3242c73b 100644 --- a/web/src/toga_web/static/toga.css +++ b/web/src/toga_web/static/toga.css @@ -106,3 +106,9 @@ main.toga.window > .container { .toga .label { white-space: nowrap; } + +/***** Table **********************************************************/ + +.toga tr.selected { + background-color: var(--sl-color-primary-200); +} diff --git a/web/src/toga_web/widgets/table.py b/web/src/toga_web/widgets/table.py index cf11e69eb6..35b61d5f00 100644 --- a/web/src/toga_web/widgets/table.py +++ b/web/src/toga_web/widgets/table.py @@ -6,10 +6,8 @@ from .base import Widget -# placeholder from gtk class TogaRow: def __init__(self, value): - super().__init__() self.value = value # All paths return none as Icon is not implemented in web. @@ -45,22 +43,13 @@ def text(self, attr, missing_value): class Table(Widget): def create(self): - self.native = self._create_native_widget( - "div", classes=["toga-table-container"] - ) - - self.table = self._create_native_widget( - "table", - ) - self.native.appendChild(self.table) + self.native = self._create_native_widget("table") - self.table_header_group = self._create_native_widget( - "thead", classes=["table-header"] - ) - self.table.appendChild(self.table_header_group) + self.table_header = self._create_native_widget("thead") + self.native.appendChild(self.table_header) - self.table_body = self._create_native_widget("tbody", classes=["table-body"]) - self.table.appendChild(self.table_body) + self.table_body = self._create_native_widget("tbody") + self.native.appendChild(self.table_body) def change_source(self, source): self.selection = {} @@ -71,10 +60,10 @@ def change_source(self, source): row_child.removeChild(td_child) self.table_body.removeChild(row_child) - for row_child in list(self.table_header_group.children): + for row_child in list(self.table_header.children): for td_child in list(row_child.children): row_child.removeChild(td_child) - self.table_header_group.removeChild(row_child) + self.table_header.removeChild(row_child) if source is not None: self._create_table_headers() @@ -82,7 +71,6 @@ def change_source(self, source): for i, row in enumerate(source): self._create_table_row(row, i) - # set table here self.refresh() def get_selection(self): @@ -96,12 +84,11 @@ def get_selection(self): def add_selection(self, index, table_row): self.selection[index] = table_row - table_row.style.backgroundColor = "lightblue" - # set colour + table_row.classList.add("selected") def remove_selection(self, index): table_row = self.selection.pop(index) - table_row.style.backgroundColor = "" + table_row.classList.remove("selected") def clear_selection(self): for index in list(self.selection): @@ -112,15 +99,13 @@ def _create_table_headers(self): headings = self.interface.headings else: headings = self.interface.accessors - self.table_header_row = self._create_native_widget( - "tr", classes=["table-header-row"] - ) + self.table_header_row = self._create_native_widget("tr") for heading in headings: th = self._create_native_widget("th", content=heading) self.table_header_row.appendChild(th) - self.table_header_group.appendChild(self.table_header_row) + self.table_header.appendChild(self.table_header_row) def _create_table_row(self, item, index): row = TogaRow(item) @@ -133,9 +118,7 @@ def _create_table_row(self, item, index): row.text(accessor, self.interface.missing_value), ] ) - tr = self._create_native_widget( - "tr", - ) + tr = self._create_native_widget("tr") tr.addEventListener( "click", create_proxy(lambda event: self.dom_row_click(event, index, tr)) @@ -147,18 +130,12 @@ def _create_table_row(self, item, index): self.table_body.appendChild(tr) def dom_row_click(self, event, index, table_row): - print("row_click listener! row:", index) if index in self.selection: self.remove_selection(index) - print("removing row ", index, " from selection") else: if not self.interface.multiple_select: self.clear_selection() self.add_selection(index, table_row) - print("adding row ", index, " to selection") - - # if self.interface.on_select: - # self.interface.on_select(self.interface) def insert(self, index, item): self.change_source(self.interface.data)