Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/3299.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added numberinput to web and textual backends.
Comment thread
LunaMeadows marked this conversation as resolved.
Outdated
12 changes: 8 additions & 4 deletions docs/reference/api/widgets/numberinput.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,17 @@ A text input that is limited to numeric input.
:align: center
:width: 300px

.. group-tab:: Web |no|
.. group-tab:: Web

Not supported
.. figure:: /reference/images/numberinput-web.png
:align: center
:width: 300px

.. group-tab:: Textual |no|
.. group-tab:: Textual

Not supported
.. figure:: /reference/images/numberinput-textual.png
:align: center
:width: 300px

Usage
-----
Expand Down
Binary file added docs/reference/images/numberinput-textual.png

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The widget screenshots should match the content of the other platforms; if you need an example to screenshot, the screenshots example app has the sample data. In this case, it should be displaying "2.71818"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also - I'm interested how you've generated this screenshot - text inputs should have a blue border around them Textual's default style.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was generated in a normal terminal on Ubuntu. I don't remember exactly what I did but I do remember that the blue border came and went semi consistently.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see how that might be happening because of focus - but you're seeing no border at all when not-focussed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I remember correctly yes. I did more testing with the textarea with making a multiline textinput but it seemed to behave similarly to the little testing I did with this input.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did confirm, when the input is not focused I have no border on any inputs. Only when it is in focus do I have a blue border

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirming I'm seeing the same thing. Looking at the other screenshots, I don't think they have focus; so we should be consistent.

However, I do see a very slightly different color in the background between the "input" and the background:

Screenshot 2025-03-31 at 12 58 22 pm

so we should try to capture that context in the screenshot if possible.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe I have the screenshot correct now? I had to pull out a different monitor cause mine were not showing the background color differences for some reason.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/reference/images/numberinput-web.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions textual/src/toga_textual/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
from .widgets.label import Label

# from .widgets.multilinetextinput import MultilineTextInput
# from .widgets.numberinput import NumberInput
from .widgets.numberinput import NumberInput

# from .widgets.optioncontainer import OptionContainer
# from .widgets.passwordinput import PasswordInput
# from .widgets.progressbar import ProgressBar
Expand Down Expand Up @@ -69,7 +70,7 @@ def not_implemented(feature):
# "ImageView",
"Label",
# "MultilineTextInput",
# "NumberInput",
"NumberInput",
# "OptionContainer",
# "PasswordInput",
# "ProgressBar",
Expand Down
84 changes: 84 additions & 0 deletions textual/src/toga_textual/widgets/numberinput.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from travertino.size import at_least

from textual.validation import Number
from textual.widgets import Input as TextualInput

from .base import Widget


class TogaInput(TextualInput):
def __init__(self, impl):
super().__init__()
self.interface = impl.interface
self.impl = impl

def on_input_changed(self, event: TextualInput.Changed) -> None:
self.interface.on_change()

def on_input_submitted(self, event: TextualInput.Submitted) -> None:
self.interface.on_confirm()

@freakboy3742 freakboy3742 Mar 30, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I press Enter when the input has focus, I get an exception because Numberinput doesn't support on_confirm()



class NumberInput(Widget):
def create(self):
self.native = TogaInput(self)
self.native.type = "number"
self.min = None
self.max = None

def get_readonly(self):
return self.native.disabled

def set_readonly(self, value):
self.native.disabled = value

def get_placeholder(self):
return self.native.placeholder

def set_placeholder(self, value):
self.native.placeholder = value

def get_value(self):
if self.native.value == "" or self.native.value is None:
return None
else:
if self.native.value != "-":
return float(self.native.value)
else:
return self.native.value

def set_value(self, value):
try:
if value is None:
self.native.value = ""
else:
self.native.value = str(value)
except AttributeError:
self.native.value = ""

def set_step(self, step):
pass

def set_min_value(self, value):
self.min = value
self.native.validators = [
Number(minimum=self.min),
]

def set_max_value(self, value):
self.max = value
self.native.validators = [
Number(maximum=self.max),
]

@property
def width_adjustment(self):
return 2

@property
def height_adjustment(self):
return 2

def rehint(self):
self.interface.intrinsic.width = at_least(len(self.native.value) + 4)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This geometry isn't working out right in my testing - it's consistently using a width of 4 characters, which won't display a value.

This is looks like it's an oversight on the TextInput class as well - the size of the widget should be clamped to a "useful minimum" (at_least(10)), rather than being tied to the width of the content in the widget.

self.interface.intrinsic.height = 3
5 changes: 3 additions & 2 deletions web/src/toga_web/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
from .widgets.label import Label

# from .widgets.multilinetextinput import MultilineTextInput
# from .widgets.numberinput import NumberInput
from .widgets.numberinput import NumberInput

# from .widgets.optioncontainer import OptionContainer
from .widgets.passwordinput import PasswordInput
from .widgets.progressbar import ProgressBar
Expand Down Expand Up @@ -66,7 +67,7 @@ def not_implemented(feature):
# 'ImageView',
"Label",
# 'MultilineTextInput',
# 'NumberInput',
"NumberInput",
# 'OptionContainer',
"PasswordInput",
"ProgressBar",
Expand Down
41 changes: 41 additions & 0 deletions web/src/toga_web/widgets/numberinput.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from .base import Widget


class NumberInput(Widget):
def create(self):
self._return_listener = None
self.native = self._create_native_widget("sl-input")
self.native.type = "number"
self.native.value = None
self.native.onkeyup = self.dom_keyup

def dom_keyup(self, event):
if event.key == "Enter":
self.interface.on_confirm()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NumberInput doesn't have an on_confirm() signal.


def get_readonly(self, value):
return self.native.readOnly

def set_readonly(self, value):
self.native.readOnly = value

def set_step(self, step):
self.native.step = step

def set_min_value(self, value):
self.native.min = value

def set_max_value(self, value):
self.native.max = value

def get_value(self):
if self.native.value == "" or self.native.value is None:
return None
else:
return float(self.native.value)

def set_value(self, value):
self.native.value = value

def set_text_align(self, value):
pass