diff --git a/changes/2176.feature.rst b/changes/2176.feature.rst new file mode 100644 index 0000000000..52db43eb84 --- /dev/null +++ b/changes/2176.feature.rst @@ -0,0 +1 @@ +DateInput and TimeInput widgets were added to the Web backend. diff --git a/docs/reference/data/widgets_by_platform.csv b/docs/reference/data/widgets_by_platform.csv index 0040fe654e..d671c8fa57 100644 --- a/docs/reference/data/widgets_by_platform.csv +++ b/docs/reference/data/widgets_by_platform.csv @@ -6,7 +6,7 @@ MainWindow,Core Component,:class:`~toga.MainWindow`,The main window of the appli ActivityIndicator,General Widget,:class:`~toga.ActivityIndicator`,A spinning activity animation,|y|,|y|,,,,|b|, Button,General Widget,:class:`~toga.Button`,Basic clickable Button,|y|,|y|,|y|,|y|,|y|,|b|,|b| Canvas,General Widget,:class:`~toga.Canvas`,A drawing area for 2D vector graphics.,|y|,|y|,|y|,|y|,|y|,, -DateInput,General Widget,:class:`~toga.DateInput`,A widget to select a calendar date,,,|y|,,|y|,, +DateInput,General Widget,:class:`~toga.DateInput`,A widget to select a calendar date,,,|y|,,|y|,|b|, DetailedList,General Widget,:class:`~toga.DetailedList`,"An ordered list of content where each item has an icon, a main heading, and a line of supplementary text.",|y|,|y|,|b|,|y|,|y|,, Divider,General Widget,:class:`~toga.Divider`,A horizontal or vertical line,|y|,|y|,|y|,,|y|,|b|, ImageView,General Widget,:class:`~toga.ImageView`,A widget that displays an image,|y|,|y|,|y|,|y|,|y|,, diff --git a/web/src/toga_web/factory.py b/web/src/toga_web/factory.py index 153c845462..c30bb2ba1e 100644 --- a/web/src/toga_web/factory.py +++ b/web/src/toga_web/factory.py @@ -11,6 +11,7 @@ from .widgets.activityindicator import ActivityIndicator from .widgets.box import Box from .widgets.button import Button +from .widgets.dateinput import DateInput from .widgets.divider import Divider # from .widgets.canvas import Canvas @@ -32,6 +33,7 @@ # from .widgets.table import Table from .widgets.textinput import TextInput +from .widgets.timeinput import TimeInput # from .widgets.tree import Tree # from .widgets.webview import WebView @@ -59,6 +61,7 @@ def not_implemented(feature): "Box", "Button", # 'Canvas', + "DateInput", "Divider", # 'DetailedList', # 'ImageView', @@ -76,6 +79,7 @@ def not_implemented(feature): "Switch", # 'Table', "TextInput", + "TimeInput", # 'Tree', # 'WebView', # 'Window', diff --git a/web/src/toga_web/widgets/dateinput.py b/web/src/toga_web/widgets/dateinput.py new file mode 100644 index 0000000000..9ccc0b2925 --- /dev/null +++ b/web/src/toga_web/widgets/dateinput.py @@ -0,0 +1,48 @@ +import datetime + +from toga_web.libs import create_proxy + +from .base import Widget + + +def py_date(native_date): + return datetime.datetime.strptime(native_date, "%Y-%m-%d").date() + + +def native_date(py_date): + return py_date.strftime("%Y-%m-%d") + + +class DateInput(Widget): + def create(self): + self._return_listener = None + self.native = self._create_native_widget("sl-input") + self.native.setAttribute("type", "date") + self.native.addEventListener("sl-change", create_proxy(self.on_change)) + + def get_value(self): + return py_date(self.native.value) + + def set_value(self, value): + if value is None: + self.native.value = "" + self.native.value = value + + def on_change(self, event): + self.interface.on_change(None) + + def get_min_date(self): + if self.native.min: + return py_date(self.native.min) + return datetime.date(1800, 1, 1) + + def get_max_date(self): + if self.native.max: + return py_date(self.native.max) + return datetime.date(8999, 12, 31) + + def set_min_date(self, value): + self.native.min = native_date(value) + + def set_max_date(self, value): + self.native.max = native_date(value) diff --git a/web/src/toga_web/widgets/timeinput.py b/web/src/toga_web/widgets/timeinput.py new file mode 100644 index 0000000000..791e2c3372 --- /dev/null +++ b/web/src/toga_web/widgets/timeinput.py @@ -0,0 +1,57 @@ +import datetime + +from toga_web.libs import create_proxy + +from .base import Widget + + +def py_time(native_time): + return datetime.time.fromisoformat(native_time) + + +def native_time(py_time): + return py_time.strftime("%H:%M") + + +class TimeInput(Widget): + def create(self): + self._return_listener = None + self.native = self._create_native_widget("sl-input") + self.native.setAttribute("type", "time") + + self.set_value(datetime.datetime.now().time().strftime("%H:%M")) + self.native.addEventListener("sl-change", create_proxy(self.on_change)) + + def on_change(self, event): + self.interface.on_change(None) + + def get_value(self): + return py_time(self.native.value) + + def set_value(self, value): + if value is None: + self.native.value = "" + self.native.value = self._format_time(value) + + def set_min_time(self, value): + self.native.min = self._format_time(value) + + def set_max_time(self, value): + self.native.max = self._format_time(value) + + def get_min_time(self): + if self.native.min: + return py_time(self.native.min) + return datetime.time(0, 0, 0) + + def get_max_time(self): + if self.native.max: + return py_time(self.native.max) + return datetime.time(23, 59, 59) + + def _format_time(self, value): + if isinstance(value, str): + value = native_time(py_time(value)) + if isinstance(value, datetime.time): + value = native_time(value) + return value