From 7f1433b88e61c7563a76386925693b6951710001 Mon Sep 17 00:00:00 2001 From: Callum Horton Date: Sun, 5 Oct 2025 13:40:15 +0800 Subject: [PATCH 1/2] Add MultilineTextInput widget to the Toga web platform. --- changes/3334.feature.rst | 1 + .../api/widgets/multilinetextinput.rst | 6 +- docs/reference/data/widgets_by_platform.csv | 2 +- .../images/multilinetextinput-web.png | Bin 0 -> 9240 bytes web/src/toga_web/factory.py | 4 +- .../toga_web/widgets/multilinetextinput.py | 116 ++++++++++++++++++ 6 files changed, 124 insertions(+), 5 deletions(-) create mode 100644 changes/3334.feature.rst create mode 100644 docs/reference/images/multilinetextinput-web.png create mode 100644 web/src/toga_web/widgets/multilinetextinput.py diff --git a/changes/3334.feature.rst b/changes/3334.feature.rst new file mode 100644 index 0000000000..c5a4b124ae --- /dev/null +++ b/changes/3334.feature.rst @@ -0,0 +1 @@ +The Toga Web backend now supports the MultilineTextInput widget. diff --git a/docs/reference/api/widgets/multilinetextinput.rst b/docs/reference/api/widgets/multilinetextinput.rst index 8c69cd4cd2..d659abc8d3 100644 --- a/docs/reference/api/widgets/multilinetextinput.rst +++ b/docs/reference/api/widgets/multilinetextinput.rst @@ -35,9 +35,11 @@ A scrollable panel that allows for the display and editing of multiple lines of :align: center :width: 300px - .. group-tab:: Web |no| + .. group-tab:: Web - Not supported + .. figure:: /reference/images/multilinetextinput-web.png + :align: center + :width: 300px .. group-tab:: Textual |no| diff --git a/docs/reference/data/widgets_by_platform.csv b/docs/reference/data/widgets_by_platform.csv index 2003efd676..af0dc13390 100644 --- a/docs/reference/data/widgets_by_platform.csv +++ b/docs/reference/data/widgets_by_platform.csv @@ -12,7 +12,7 @@ Divider,General Widget,:class:`~toga.Divider`,A horizontal or vertical line,|y|, ImageView,General Widget,:class:`~toga.ImageView`,A widget that displays an image,|y|,|y|,|y|,|y|,|y|,, Label,General Widget,:class:`~toga.Label`,Text label,|y|,|y|,|y|,|y|,|y|,|b|,|b| MapView,General Widget,:class:`~toga.MapView`,A zoomable map that can be annotated with location pins,|y|,|y|,|y|,|y|,|y|,, -MultilineTextInput,General Widget,:class:`~toga.MultilineTextInput`,Multi-line Text Input field,|y|,|y|,|y|,|y|,|y|,, +MultilineTextInput,General Widget,:class:`~toga.MultilineTextInput`,Multi-line Text Input field,|y|,|y|,|y|,|y|,|y|,|b|, NumberInput,General Widget,:class:`~toga.NumberInput`,A text input that is limited to numeric input,|y|,|y|,|y|,|y|,|y|,, PasswordInput,General Widget,:class:`~toga.PasswordInput`,A text input that hides its input,|y|,|y|,|y|,|y|,|y|,|b|, ProgressBar,General Widget,:class:`~toga.ProgressBar`,Progress Bar,|y|,|y|,|y|,|y|,|y|,|b|, diff --git a/docs/reference/images/multilinetextinput-web.png b/docs/reference/images/multilinetextinput-web.png new file mode 100644 index 0000000000000000000000000000000000000000..410ac2e4e409ec8ba0fc2d1d9d43b4d9ac16c926 GIT binary patch literal 9240 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 z z>WQp96mZe{Mv0NdZsPzjFr2&}m4&?gm+Tdp{V(wd-LcFG&6VfC>G58V_gV0KRlU6y2a?Pd zFi#gs_s*TjoG}KhxwyWyU1P zv5Ke}B*4Lu)(hTIQ`b$1g2Jc0zMfq#{bqr%bJ#R!^8lRKBbz-u(yt&*GfdZ0oN{O?Yuhg@yF)0O z)Fcf(sjx9M?LzqOn+blatk_!{)kkpyf(k$d`{zJ_P67`;ey7Nno3(mhKfZSKvCA=HKAxye+kPSNf0$2j>i5y?FN-QuFP9EF1kMdc ztjt{zb;%pC^sq`GphHtcV?vgOk`}{JcWDaln`|J<0+vhCU@~?`4loEX9Eg2aQQ2hS z+kxc77T{9Fbg#PP_{I3?#gT6XXpY&Py60D6$BBvY{OO9iZrA*VOT^g=L>&iyWa)_D z5U(0@4|=udg$86Gs1=O?U!aE@){(R)?n z{m{W#G=R^5QGfC`z3Ov8_*&a7I5VJm2~Xw&5U2k)`*5%&p=)xnz*xKN2PIcaJv2F0 zmL5M{-HxSwNt@k82Oy@?SC4A(A^9Yv(jdu=k%q5R)+{{rifF%Uv5pRQH7&G8zO?sv zr=R@}Fn-`#$IZYO>m1^N@-^nm_H_E~u>>B*URr8R#rBCP0~avYtPulKjq735LAv3> z&Y>?3zvn8WMy;CUSINR2z>)Ux<4kYRQB8=MEd3{`>vSPR0F8liWi_QVLV%{{TwgeC zae0`TDd-QCusU=~aJ=fY4CmR+nd`Uqe$Ltb9G`QKR&+&Y9_nrj$Y(@dvN&p%q?Q$$~9{-ioSL6MgiRlmw;QeMXF{z7XwebnJohj3#FcO) zSN^BXQ<98Q-n=%jR5_tvoCbpSzp=|Ch$S21ACw#7Mgl~F! z(Hfyda+8b{mL%68eDNj^2ePC~meV0-q-4rYXQDB!q5k-b+VB-IVg|f~C+{V>v*{aL zP0d$eW5R(#A&7ruLNaLU<%ZuBlK%Y$>3jCmVFsN);P7)pop;1|2hkSRZAw|%Ur6F_ zSaVZ%6sqf%k{xNf|2DMwgTr~-+=_DnR#`Ok*5Ei`RU|T*3{D_*hqmZ*tH_^17rbj? zo9*^S^+hy2OGLy)l~_@~`Ce)=HX>fJrzsP4%_I00z~wzZSVf6;m_E4gPm$iy&)}ZN zzcijqY%=i3ypm@pfoYc=)8y_C%fR%5BUHncL4BoyG3LsQS!LEg#Riini00bC|NBhG zc5&Byu<=Ft39aS7sk_u?T;9MzY%~6I3$m{1N`wnH6DCc0Qiey| zqrCw=)q~;}N1W*xdWU5B+~}&RN6h>5Nrjh3%zBIbtr*}P|2e}^3TY*dGgZFlk3%d7 zrKAtltMLc&MX%FZx8y&^PA~%z7n64``z|eaKJmGP`aUI4jGa;H6rA{XhfCONzba_| z!)CgE#)-20B{^MYbpHd_{5P%hXEgtt=lrh@*N+A`0Em;^#slIs50bLHx?F{fk>7s+ D8&nBd literal 0 HcmV?d00001 diff --git a/web/src/toga_web/factory.py b/web/src/toga_web/factory.py index 17b0d1fab1..78831a82aa 100644 --- a/web/src/toga_web/factory.py +++ b/web/src/toga_web/factory.py @@ -19,8 +19,8 @@ # from .widgets.detailedlist import DetailedList # from .widgets.imageview import ImageView from .widgets.label import Label +from .widgets.multilinetextinput import MultilineTextInput -# from .widgets.multilinetextinput import MultilineTextInput # from .widgets.numberinput import NumberInput # from .widgets.optioncontainer import OptionContainer from .widgets.passwordinput import PasswordInput @@ -69,7 +69,7 @@ def not_implemented(feature): # 'DetailedList', # 'ImageView', "Label", - # 'MultilineTextInput', + "MultilineTextInput", # 'NumberInput', # 'OptionContainer', "PasswordInput", diff --git a/web/src/toga_web/widgets/multilinetextinput.py b/web/src/toga_web/widgets/multilinetextinput.py new file mode 100644 index 0000000000..9d6046ca8f --- /dev/null +++ b/web/src/toga_web/widgets/multilinetextinput.py @@ -0,0 +1,116 @@ +from travertino.colors import TRANSPARENT + +from toga_web.libs import create_proxy + +from .base import Widget + + +class MultilineTextInput(Widget): + def create(self): + self.native = self._create_native_widget("sl-textarea") + self.native.addEventListener("sl-input", create_proxy(self._on_input)) + + def _after_render(_): + self.native.shadowRoot.querySelector("textarea").style.resize = "none" + + self.native.updateComplete.then(create_proxy(_after_render)) + + def _on_input(self, event): + self.interface.on_change() + + def get_value(self): + return self.native.value or "" + + def set_value(self, value): + self.native.value = "" if value is None else str(value) + self.interface.on_change() + + def get_placeholder(self): + return self.native.placeholder or "" + + def set_placeholder(self, value): + self.native.placeholder = value or "" + + def get_readonly(self): + return bool(self.native.readonly) + + def set_readonly(self, value): + self.native.readonly = bool(value) + + def get_enabled(self): + return not bool(self.native.disabled) + + def set_enabled(self, value): + self.native.disabled = not bool(value) + + def _to_css_color(self, value: object) -> str: + if value is None or value is TRANSPARENT: + return "" + try: + return str(value) + except Exception: + return "" + + def set_background_color(self, color): + css = self._to_css_color(color) + + def _apply(_): + inner = self.native.shadowRoot.querySelector("textarea") + if inner: + if css: + inner.style.setProperty("background", css) + else: + inner.style.removeProperty("background") + + self.native.updateComplete.then(create_proxy(_apply)) + + def set_color(self, color): + css = self._to_css_color(color) + + def _apply(_): + inner = self.native.shadowRoot.querySelector("textarea") + if inner: + if css: + inner.style.setProperty("color", css) + else: + inner.style.removeProperty("color") + + self.native.updateComplete.then(create_proxy(_apply)) + + def set_text_align(self, value): + mapping = {0: "left", 1: "right", 2: "center", 3: "justify"} + css_align = mapping.get(value, value if isinstance(value, str) else "left") + + def _apply(_): + inner = self.native.shadowRoot.querySelector("textarea") + if inner: + inner.style.textAlign = css_align + + self.native.updateComplete.then(create_proxy(_apply)) + + def set_font(self, font): + pass + + def rehint(self): + self.interface.intrinsic.width = max( + self.interface.intrinsic.width, self.interface._MIN_WIDTH + ) + self.interface.intrinsic.height = max( + self.interface.intrinsic.height, self.interface._MIN_HEIGHT + ) + + def scroll_to_top(self): + def _go(_): + inner = self.native.shadowRoot.querySelector("textarea") + if inner: + inner.scrollTop = 0 + + self.native.updateComplete.then(create_proxy(_go)) + + def scroll_to_bottom(self): + def _go(_): + inner = self.native.shadowRoot.querySelector("textarea") + if inner: + inner.scrollTop = max(0, inner.scrollHeight - inner.clientHeight) + + self.native.updateComplete.then(create_proxy(_go)) From 1983790d5fbc365d95b1517c96ad29d71756d2f3 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 7 Oct 2025 09:21:13 +0800 Subject: [PATCH 2/2] Relax size constraints on multilinetextinput. --- web/src/toga_web/widgets/multilinetextinput.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/web/src/toga_web/widgets/multilinetextinput.py b/web/src/toga_web/widgets/multilinetextinput.py index 9d6046ca8f..50acf23d28 100644 --- a/web/src/toga_web/widgets/multilinetextinput.py +++ b/web/src/toga_web/widgets/multilinetextinput.py @@ -1,4 +1,5 @@ from travertino.colors import TRANSPARENT +from travertino.size import at_least from toga_web.libs import create_proxy @@ -92,12 +93,8 @@ def set_font(self, font): pass def rehint(self): - self.interface.intrinsic.width = max( - self.interface.intrinsic.width, self.interface._MIN_WIDTH - ) - self.interface.intrinsic.height = max( - self.interface.intrinsic.height, self.interface._MIN_HEIGHT - ) + self.interface.intrinsic.width = at_least(self.interface._MIN_WIDTH) + self.interface.intrinsic.height = at_least(self.interface._MIN_HEIGHT) def scroll_to_top(self): def _go(_):