We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
toga-winforms v0.3.0.dev38 gives Error on Windows app
Toga-winforms v0.3.0.dev38 gives AttributeError: 'TextInput' object has not attribute '_on_change'

**To Reproduce**
1. Create an app that uses toga.TextInput(),
2. Set toga-winforms==0.3.0.dev38 under Windows requirements in pyproject.toml file.
3. Update dependencies: `update -d`
4. Build and run app on Windows

**Screenshots**
![attribute_error](https://user-images.githubusercontent.com/6691022/194653140-a06a8ef9-cab7-41a5-907e-b5126a2e8507.PNG)

**Environment:**
 - Operating System: Windows 10
 - Python version: 3.8.10
 - Software versions:
   - Briefcase: 0.3.9
   - Toga: 0.3.0.dev38


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `examples/textinput/textinput/app.py`
Content:
```
1 from string import ascii_lowercase, ascii_uppercase, digits
2 
3 import toga
4 from toga.constants import COLUMN
5 from toga.style import Pack
6 from toga import validators
7 
8 EMPTY_PASSWORD = 'Empty password'
9 
10 
11 class TextInputApp(toga.App):
12     # Button callback functions
13     def do_extract_values(self, widget, **kwargs):
14         # Disable all the text inputs
15         self.text_input.enabled = False
16         self.password_input.enabled = False
17         self.number_input.enabled = False
18 
19         # Update the labels with the extracted values
20         self.text_label.text = "Text content: {}".format(
21             self.text_input.value
22         )
23 
24         self.password_label.text = "Your password is {}: {}".format(
25             "valid" if self.password_input.is_valid else "invalid",
26             self.password_input.value
27         )
28 
29         number = self.number_input.value
30         if number:
31             self.number_label.text = "Double the number is: {}".format(number * 2)
32         else:
33             self.number_label.text = "You didn't enter a number"
34 
35         # Wait 5 seconds
36         for i in range(5, 0, -1):
37             self.label.text = 'Counting down from {}...'.format(i)
38             yield 1
39         self.label.text = 'Enter some values and press extract.'
40 
41         # Renable the inputs again.
42         self.text_input.enabled = True
43         self.password_input.enabled = True
44         self.number_input.enabled = True
45 
46     def startup(self):
47         # Set up main window
48         self.main_window = toga.MainWindow(title=self.name)
49 
50         # Labels to show responses.
51         self.label = toga.Label(
52             'Enter some values and press extract.', style=Pack(padding=10)
53         )
54         self.text_label = toga.Label('Ready.', style=Pack(padding=10))
55         self.password_label = toga.Label('Ready.', style=Pack(padding=10))
56         self.password_content_label = toga.Label(
57             EMPTY_PASSWORD, style=Pack(padding_bottom=10, font_size=9)
58         )
59         self.number_label = toga.Label('Ready.', style=Pack(padding=10))
60 
61         # Text inputs and a button
62         self.text_input = toga.TextInput(
63             placeholder='Type something...', style=Pack(padding=10)
64         )
65         self.password_input = toga.PasswordInput(
66             placeholder='Password...',
67             style=Pack(padding=10),
68             on_change=self.on_password_change,
69             validators=[
70                 validators.MinLength(10),
71                 validators.ContainsUppercase(),
72                 validators.ContainsLowercase(),
73                 validators.ContainsSpecial(),
74                 validators.ContainsDigit()
75             ]
76         )
77         self.email_input = toga.TextInput(
78             placeholder='Email...',
79             style=Pack(padding=10),
80             validators=[validators.Email()]
81         )
82         self.number_input = toga.NumberInput(style=Pack(padding=10))
83         btn_extract = toga.Button(
84             'Extract values',
85             on_press=self.do_extract_values,
86             style=Pack(flex=1),
87         )
88 
89         # Outermost box
90         box = toga.Box(
91             children=[
92                 self.label,
93                 self.text_input,
94                 self.password_input,
95                 self.password_content_label,
96                 self.email_input,
97                 self.number_input,
98                 self.text_label,
99                 self.password_label,
100                 self.number_label,
101                 btn_extract,
102             ],
103             style=Pack(
104                 flex=1,
105                 direction=COLUMN,
106                 padding=10,
107             )
108         )
109 
110         # Add the content on the main window
111         self.main_window.content = box
112 
113         # Show the main window
114         self.main_window.show()
115 
116     def on_password_change(self, widget):
117         content = widget.value
118         self.password_content_label.text = self.get_password_content_label(content)
119 
120     def get_password_content_label(self, content):
121         if content.strip() == "":
122             return EMPTY_PASSWORD
123         contains = set()
124         for letter in content:
125             if letter in ascii_uppercase:
126                 contains.add("uppercase letters")
127             elif letter in ascii_lowercase:
128                 contains.add("lowercase letters")
129             elif letter in digits:
130                 contains.add("digits")
131             else:
132                 contains.add("special characters")
133         return "Password contains: {}".format(', '.join(contains))
134 
135 
136 def main():
137     return TextInputApp('TextInput', 'org.beeware.widgets.textinput')
138 
139 
140 if __name__ == '__main__':
141     app = main()
142     app.main_loop()
143
```

Path: `src/core/toga/widgets/textinput.py`
Content:
```
1 import warnings
2 
3 from toga.handlers import wrapped_handler
4 
5 from .base import Widget
6 
7 
8 class TextInput(Widget):
9     """ A widget get user input.
10 
11     Args:
12         id (str): An identifier for this widget.
13         style (:obj:`Style`): An optional style object. If no style is provided then
14             a new one will be created for the widget.
15         factory (:obj:`module`): A python module that is capable to return a
16             implementation of this class with the same name. (optional & normally not needed)
17         value (str): The initial text for the input.
18         placeholder (str): If no input is present this text is shown.
19         readonly (bool):  Whether a user can write into the text input, defaults to `False`.
20         on_change (Callable): Method to be called when text is changed in text box
21         validators (list): list of validators to run on the value of the text box. Should
22             return None is value is valid and an error message if not.
23         on_change (``callable``): The handler to invoke when the text changes.
24         on_gain_focus (:obj:`callable`): Function to execute when get focused.
25         on_lose_focus (:obj:`callable`): Function to execute when lose focus.
26     """
27     MIN_WIDTH = 100
28 
29     def __init__(
30         self,
31         id=None,
32         style=None,
33         factory=None,
34         value=None,
35         placeholder=None,
36         readonly=False,
37         on_change=None,
38         on_gain_focus=None,
39         on_lose_focus=None,
40         validators=None,
41         initial=None,  # DEPRECATED!
42     ):
43         super().__init__(id=id, style=style, factory=factory)
44 
45         # Create a platform specific implementation of the widget
46         self._create()
47 
48         ##################################################################
49         # 2022-07: Backwards compatibility
50         ##################################################################
51 
52         # initial replaced with value
53         if initial is not None:
54             if value is not None:
55                 raise ValueError(
56                     "Cannot specify both `initial` and `value`; "
57                     "`initial` has been deprecated, use `value`"
58                 )
59             else:
60                 warnings.warn(
61                     "`initial` has been renamed `value`", DeprecationWarning
62                 )
63             value = initial
64 
65         ##################################################################
66         # End backwards compatibility.
67         ##################################################################
68 
69         self.placeholder = placeholder
70         self.readonly = readonly
71 
72         # Set the actual value before on_change, because we do not want on_change triggered by it
73         self.value = value
74         self.on_change = on_change
75         self.validators = validators
76         self.on_lose_focus = on_lose_focus
77         self.on_gain_focus = on_gain_focus
78 
79     def _create(self):
80         self._impl = self.factory.TextInput(interface=self)
81 
82     @property
83     def readonly(self):
84         """ Whether a user can write into the text input
85 
86         Returns:
87             ``True`` if only read is possible.
88             ``False`` if read and write is possible.
89         """
90         return self._readonly
91 
92     @readonly.setter
93     def readonly(self, value):
94         self._readonly = value
95         self._impl.set_readonly(value)
96 
97     @property
98     def placeholder(self):
99         """ The placeholder text.
100 
101         Returns:
102             The placeholder text as a ``str``.
103         """
104         return self._placeholder
105 
106     @placeholder.setter
107     def placeholder(self, value):
108         if value is None:
109             self._placeholder = ''
110         else:
111             self._placeholder = str(value)
112         self._impl.set_placeholder(value)
113 
114     @property
115     def value(self):
116         """ The value of the text input field
117 
118         Returns:
119             The current text of the widget as a ``str``.
120         """
121         return self._impl.get_value()
122 
123     @value.setter
124     def value(self, value):
125         if value is None:
126             v = ''
127         else:
128             v = str(value)
129         self._impl.set_value(v)
130 
131     @property
132     def is_valid(self):
133         return self._impl.is_valid()
134 
135     def clear(self):
136         """ Clears the text of the widget """
137         self.value = ''
138 
139     @property
140     def on_change(self):
141         """The handler to invoke when the value changes
142 
143         Returns:
144             The function ``callable`` that is called on a content change.
145         """
146         return self._on_change
147 
148     @on_change.setter
149     def on_change(self, handler):
150         """Set the handler to invoke when the value is changed.
151 
152         Args:
153             handler (:obj:`callable`): The handler to invoke when the value is changed.
154         """
155         self._on_change = wrapped_handler(self, handler)
156         self._impl.set_on_change(self._on_change)
157 
158     @property
159     def validators(self):
160         return self._validators
161 
162     @validators.setter
163     def validators(self, validators):
164         if validators is None:
165             self._validators = []
166         else:
167             self._validators = validators
168         self.validate()
169 
170     @property
171     def on_gain_focus(self):
172         """The handler to invoke when the widget get focus.
173 
174         Returns:
175             The function ``callable`` that is called on widget focus gain.
176         """
177         return self._on_gain_focus
178 
179     @on_gain_focus.setter
180     def on_gain_focus(self, handler):
181         self._on_gain_focus = wrapped_handler(self, handler)
182         self._impl.set_on_gain_focus(self._on_gain_focus)
183 
184     @property
185     def on_lose_focus(self):
186         """The handler to invoke when the widget lose focus.
187 
188         Returns:
189             The function ``callable`` that is called on widget focus loss.
190         """
191         return self._on_lose_focus
192 
193     @on_lose_focus.setter
194     def on_lose_focus(self, handler):
195         self._on_lose_focus = wrapped_handler(self, handler)
196         self._impl.set_on_lose_focus(self._on_lose_focus)
197 
198     def validate(self):
199         error_message = None
200         for validator in self.validators:
201             if error_message is None:
202                 error_message = validator(self.value)
203 
204         if error_message is None:
205             self._impl.clear_error()
206             return True
207         else:
208             self._impl.set_error(error_message)
209             return False
210
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/examples/textinput/textinput/app.py b/examples/textinput/textinput/app.py
--- a/examples/textinput/textinput/app.py
+++ b/examples/textinput/textinput/app.py
@@ -13,12 +13,14 @@
     def do_extract_values(self, widget, **kwargs):
         # Disable all the text inputs
         self.text_input.enabled = False
+        self.text_input_placeholder.enabled = False
         self.password_input.enabled = False
         self.number_input.enabled = False
 
         # Update the labels with the extracted values
-        self.text_label.text = "Text content: {}".format(
-            self.text_input.value
+        self.text_label.text = "Text content: {}; {}".format(
+            self.text_input.value,
+            self.text_input_placeholder.value,
         )
 
         self.password_label.text = "Your password is {}: {}".format(
@@ -40,6 +42,7 @@
 
         # Renable the inputs again.
         self.text_input.enabled = True
+        self.text_input_placeholder.enabled = True
         self.password_input.enabled = True
         self.number_input.enabled = True
 
@@ -60,6 +63,10 @@
 
         # Text inputs and a button
         self.text_input = toga.TextInput(
+            value='Initial value',
+            placeholder='Type something...', style=Pack(padding=10)
+        )
+        self.text_input_placeholder = toga.TextInput(
             placeholder='Type something...', style=Pack(padding=10)
         )
         self.password_input = toga.PasswordInput(
@@ -91,6 +98,7 @@
             children=[
                 self.label,
                 self.text_input,
+                self.text_input_placeholder,
                 self.password_input,
                 self.password_content_label,
                 self.email_input,
diff --git a/src/core/toga/widgets/textinput.py b/src/core/toga/widgets/textinput.py
--- a/src/core/toga/widgets/textinput.py
+++ b/src/core/toga/widgets/textinput.py
@@ -70,7 +70,10 @@
         self.readonly = readonly
 
         # Set the actual value before on_change, because we do not want on_change triggered by it
+        # However, we need to prime the handler property in case it is accessed.
+        self._on_change = None
         self.value = value
+
         self.on_change = on_change
         self.validators = validators
         self.on_lose_focus = on_lose_focus

{"golden_diff": "diff --git a/examples/textinput/textinput/app.py b/examples/textinput/textinput/app.py\n--- a/examples/textinput/textinput/app.py\n+++ b/examples/textinput/textinput/app.py\n@@ -13,12 +13,14 @@\n     def do_extract_values(self, widget, **kwargs):\n         # Disable all the text inputs\n         self.text_input.enabled = False\n+        self.text_input_placeholder.enabled = False\n         self.password_input.enabled = False\n         self.number_input.enabled = False\n \n         # Update the labels with the extracted values\n-        self.text_label.text = \"Text content: {}\".format(\n-            self.text_input.value\n+        self.text_label.text = \"Text content: {}; {}\".format(\n+            self.text_input.value,\n+            self.text_input_placeholder.value,\n         )\n \n         self.password_label.text = \"Your password is {}: {}\".format(\n@@ -40,6 +42,7 @@\n \n         # Renable the inputs again.\n         self.text_input.enabled = True\n+        self.text_input_placeholder.enabled = True\n         self.password_input.enabled = True\n         self.number_input.enabled = True\n \n@@ -60,6 +63,10 @@\n \n         # Text inputs and a button\n         self.text_input = toga.TextInput(\n+            value='Initial value',\n+            placeholder='Type something...', style=Pack(padding=10)\n+        )\n+        self.text_input_placeholder = toga.TextInput(\n             placeholder='Type something...', style=Pack(padding=10)\n         )\n         self.password_input = toga.PasswordInput(\n@@ -91,6 +98,7 @@\n             children=[\n                 self.label,\n                 self.text_input,\n+                self.text_input_placeholder,\n                 self.password_input,\n                 self.password_content_label,\n                 self.email_input,\ndiff --git a/src/core/toga/widgets/textinput.py b/src/core/toga/widgets/textinput.py\n--- a/src/core/toga/widgets/textinput.py\n+++ b/src/core/toga/widgets/textinput.py\n@@ -70,7 +70,10 @@\n         self.readonly = readonly\n \n         # Set the actual value before on_change, because we do not want on_change triggered by it\n+        # However, we need to prime the handler property in case it is accessed.\n+        self._on_change = None\n         self.value = value\n+\n         self.on_change = on_change\n         self.validators = validators\n         self.on_lose_focus = on_lose_focus\n", "issue": "toga-winforms v0.3.0.dev38 gives Error on Windows app\nToga-winforms v0.3.0.dev38 gives AttributeError: 'TextInput' object has not attribute '_on_change'\r\n\r\n**To Reproduce**\r\n1. Create an app that uses toga.TextInput(),\r\n2. Set toga-winforms==0.3.0.dev38 under Windows requirements in pyproject.toml file.\r\n3. Update dependencies: `update -d`\r\n4. Build and run app on Windows\r\n\r\n**Screenshots**\r\n![attribute_error](https://user-images.githubusercontent.com/6691022/194653140-a06a8ef9-cab7-41a5-907e-b5126a2e8507.PNG)\r\n\r\n**Environment:**\r\n - Operating System: Windows 10\r\n - Python version: 3.8.10\r\n - Software versions:\r\n   - Briefcase: 0.3.9\r\n   - Toga: 0.3.0.dev38\r\n\n", "before_files": [{"content": "from string import ascii_lowercase, ascii_uppercase, digits\n\nimport toga\nfrom toga.constants import COLUMN\nfrom toga.style import Pack\nfrom toga import validators\n\nEMPTY_PASSWORD = 'Empty password'\n\n\nclass TextInputApp(toga.App):\n    # Button callback functions\n    def do_extract_values(self, widget, **kwargs):\n        # Disable all the text inputs\n        self.text_input.enabled = False\n        self.password_input.enabled = False\n        self.number_input.enabled = False\n\n        # Update the labels with the extracted values\n        self.text_label.text = \"Text content: {}\".format(\n            self.text_input.value\n        )\n\n        self.password_label.text = \"Your password is {}: {}\".format(\n            \"valid\" if self.password_input.is_valid else \"invalid\",\n            self.password_input.value\n        )\n\n        number = self.number_input.value\n        if number:\n            self.number_label.text = \"Double the number is: {}\".format(number * 2)\n        else:\n            self.number_label.text = \"You didn't enter a number\"\n\n        # Wait 5 seconds\n        for i in range(5, 0, -1):\n            self.label.text = 'Counting down from {}...'.format(i)\n            yield 1\n        self.label.text = 'Enter some values and press extract.'\n\n        # Renable the inputs again.\n        self.text_input.enabled = True\n        self.password_input.enabled = True\n        self.number_input.enabled = True\n\n    def startup(self):\n        # Set up main window\n        self.main_window = toga.MainWindow(title=self.name)\n\n        # Labels to show responses.\n        self.label = toga.Label(\n            'Enter some values and press extract.', style=Pack(padding=10)\n        )\n        self.text_label = toga.Label('Ready.', style=Pack(padding=10))\n        self.password_label = toga.Label('Ready.', style=Pack(padding=10))\n        self.password_content_label = toga.Label(\n            EMPTY_PASSWORD, style=Pack(padding_bottom=10, font_size=9)\n        )\n        self.number_label = toga.Label('Ready.', style=Pack(padding=10))\n\n        # Text inputs and a button\n        self.text_input = toga.TextInput(\n            placeholder='Type something...', style=Pack(padding=10)\n        )\n        self.password_input = toga.PasswordInput(\n            placeholder='Password...',\n            style=Pack(padding=10),\n            on_change=self.on_password_change,\n            validators=[\n                validators.MinLength(10),\n                validators.ContainsUppercase(),\n                validators.ContainsLowercase(),\n                validators.ContainsSpecial(),\n                validators.ContainsDigit()\n            ]\n        )\n        self.email_input = toga.TextInput(\n            placeholder='Email...',\n            style=Pack(padding=10),\n            validators=[validators.Email()]\n        )\n        self.number_input = toga.NumberInput(style=Pack(padding=10))\n        btn_extract = toga.Button(\n            'Extract values',\n            on_press=self.do_extract_values,\n            style=Pack(flex=1),\n        )\n\n        # Outermost box\n        box = toga.Box(\n            children=[\n                self.label,\n                self.text_input,\n                self.password_input,\n                self.password_content_label,\n                self.email_input,\n                self.number_input,\n                self.text_label,\n                self.password_label,\n                self.number_label,\n                btn_extract,\n            ],\n            style=Pack(\n                flex=1,\n                direction=COLUMN,\n                padding=10,\n            )\n        )\n\n        # Add the content on the main window\n        self.main_window.content = box\n\n        # Show the main window\n        self.main_window.show()\n\n    def on_password_change(self, widget):\n        content = widget.value\n        self.password_content_label.text = self.get_password_content_label(content)\n\n    def get_password_content_label(self, content):\n        if content.strip() == \"\":\n            return EMPTY_PASSWORD\n        contains = set()\n        for letter in content:\n            if letter in ascii_uppercase:\n                contains.add(\"uppercase letters\")\n            elif letter in ascii_lowercase:\n                contains.add(\"lowercase letters\")\n            elif letter in digits:\n                contains.add(\"digits\")\n            else:\n                contains.add(\"special characters\")\n        return \"Password contains: {}\".format(', '.join(contains))\n\n\ndef main():\n    return TextInputApp('TextInput', 'org.beeware.widgets.textinput')\n\n\nif __name__ == '__main__':\n    app = main()\n    app.main_loop()\n", "path": "examples/textinput/textinput/app.py"}, {"content": "import warnings\n\nfrom toga.handlers import wrapped_handler\n\nfrom .base import Widget\n\n\nclass TextInput(Widget):\n    \"\"\" A widget get user input.\n\n    Args:\n        id (str): An identifier for this widget.\n        style (:obj:`Style`): An optional style object. If no style is provided then\n            a new one will be created for the widget.\n        factory (:obj:`module`): A python module that is capable to return a\n            implementation of this class with the same name. (optional & normally not needed)\n        value (str): The initial text for the input.\n        placeholder (str): If no input is present this text is shown.\n        readonly (bool):  Whether a user can write into the text input, defaults to `False`.\n        on_change (Callable): Method to be called when text is changed in text box\n        validators (list): list of validators to run on the value of the text box. Should\n            return None is value is valid and an error message if not.\n        on_change (``callable``): The handler to invoke when the text changes.\n        on_gain_focus (:obj:`callable`): Function to execute when get focused.\n        on_lose_focus (:obj:`callable`): Function to execute when lose focus.\n    \"\"\"\n    MIN_WIDTH = 100\n\n    def __init__(\n        self,\n        id=None,\n        style=None,\n        factory=None,\n        value=None,\n        placeholder=None,\n        readonly=False,\n        on_change=None,\n        on_gain_focus=None,\n        on_lose_focus=None,\n        validators=None,\n        initial=None,  # DEPRECATED!\n    ):\n        super().__init__(id=id, style=style, factory=factory)\n\n        # Create a platform specific implementation of the widget\n        self._create()\n\n        ##################################################################\n        # 2022-07: Backwards compatibility\n        ##################################################################\n\n        # initial replaced with value\n        if initial is not None:\n            if value is not None:\n                raise ValueError(\n                    \"Cannot specify both `initial` and `value`; \"\n                    \"`initial` has been deprecated, use `value`\"\n                )\n            else:\n                warnings.warn(\n                    \"`initial` has been renamed `value`\", DeprecationWarning\n                )\n            value = initial\n\n        ##################################################################\n        # End backwards compatibility.\n        ##################################################################\n\n        self.placeholder = placeholder\n        self.readonly = readonly\n\n        # Set the actual value before on_change, because we do not want on_change triggered by it\n        self.value = value\n        self.on_change = on_change\n        self.validators = validators\n        self.on_lose_focus = on_lose_focus\n        self.on_gain_focus = on_gain_focus\n\n    def _create(self):\n        self._impl = self.factory.TextInput(interface=self)\n\n    @property\n    def readonly(self):\n        \"\"\" Whether a user can write into the text input\n\n        Returns:\n            ``True`` if only read is possible.\n            ``False`` if read and write is possible.\n        \"\"\"\n        return self._readonly\n\n    @readonly.setter\n    def readonly(self, value):\n        self._readonly = value\n        self._impl.set_readonly(value)\n\n    @property\n    def placeholder(self):\n        \"\"\" The placeholder text.\n\n        Returns:\n            The placeholder text as a ``str``.\n        \"\"\"\n        return self._placeholder\n\n    @placeholder.setter\n    def placeholder(self, value):\n        if value is None:\n            self._placeholder = ''\n        else:\n            self._placeholder = str(value)\n        self._impl.set_placeholder(value)\n\n    @property\n    def value(self):\n        \"\"\" The value of the text input field\n\n        Returns:\n            The current text of the widget as a ``str``.\n        \"\"\"\n        return self._impl.get_value()\n\n    @value.setter\n    def value(self, value):\n        if value is None:\n            v = ''\n        else:\n            v = str(value)\n        self._impl.set_value(v)\n\n    @property\n    def is_valid(self):\n        return self._impl.is_valid()\n\n    def clear(self):\n        \"\"\" Clears the text of the widget \"\"\"\n        self.value = ''\n\n    @property\n    def on_change(self):\n        \"\"\"The handler to invoke when the value changes\n\n        Returns:\n            The function ``callable`` that is called on a content change.\n        \"\"\"\n        return self._on_change\n\n    @on_change.setter\n    def on_change(self, handler):\n        \"\"\"Set the handler to invoke when the value is changed.\n\n        Args:\n            handler (:obj:`callable`): The handler to invoke when the value is changed.\n        \"\"\"\n        self._on_change = wrapped_handler(self, handler)\n        self._impl.set_on_change(self._on_change)\n\n    @property\n    def validators(self):\n        return self._validators\n\n    @validators.setter\n    def validators(self, validators):\n        if validators is None:\n            self._validators = []\n        else:\n            self._validators = validators\n        self.validate()\n\n    @property\n    def on_gain_focus(self):\n        \"\"\"The handler to invoke when the widget get focus.\n\n        Returns:\n            The function ``callable`` that is called on widget focus gain.\n        \"\"\"\n        return self._on_gain_focus\n\n    @on_gain_focus.setter\n    def on_gain_focus(self, handler):\n        self._on_gain_focus = wrapped_handler(self, handler)\n        self._impl.set_on_gain_focus(self._on_gain_focus)\n\n    @property\n    def on_lose_focus(self):\n        \"\"\"The handler to invoke when the widget lose focus.\n\n        Returns:\n            The function ``callable`` that is called on widget focus loss.\n        \"\"\"\n        return self._on_lose_focus\n\n    @on_lose_focus.setter\n    def on_lose_focus(self, handler):\n        self._on_lose_focus = wrapped_handler(self, handler)\n        self._impl.set_on_lose_focus(self._on_lose_focus)\n\n    def validate(self):\n        error_message = None\n        for validator in self.validators:\n            if error_message is None:\n                error_message = validator(self.value)\n\n        if error_message is None:\n            self._impl.clear_error()\n            return True\n        else:\n            self._impl.set_error(error_message)\n            return False\n", "path": "src/core/toga/widgets/textinput.py"}], "after_files": [{"content": "from string import ascii_lowercase, ascii_uppercase, digits\n\nimport toga\nfrom toga.constants import COLUMN\nfrom toga.style import Pack\nfrom toga import validators\n\nEMPTY_PASSWORD = 'Empty password'\n\n\nclass TextInputApp(toga.App):\n    # Button callback functions\n    def do_extract_values(self, widget, **kwargs):\n        # Disable all the text inputs\n        self.text_input.enabled = False\n        self.text_input_placeholder.enabled = False\n        self.password_input.enabled = False\n        self.number_input.enabled = False\n\n        # Update the labels with the extracted values\n        self.text_label.text = \"Text content: {}; {}\".format(\n            self.text_input.value,\n            self.text_input_placeholder.value,\n        )\n\n        self.password_label.text = \"Your password is {}: {}\".format(\n            \"valid\" if self.password_input.is_valid else \"invalid\",\n            self.password_input.value\n        )\n\n        number = self.number_input.value\n        if number:\n            self.number_label.text = \"Double the number is: {}\".format(number * 2)\n        else:\n            self.number_label.text = \"You didn't enter a number\"\n\n        # Wait 5 seconds\n        for i in range(5, 0, -1):\n            self.label.text = 'Counting down from {}...'.format(i)\n            yield 1\n        self.label.text = 'Enter some values and press extract.'\n\n        # Renable the inputs again.\n        self.text_input.enabled = True\n        self.text_input_placeholder.enabled = True\n        self.password_input.enabled = True\n        self.number_input.enabled = True\n\n    def startup(self):\n        # Set up main window\n        self.main_window = toga.MainWindow(title=self.name)\n\n        # Labels to show responses.\n        self.label = toga.Label(\n            'Enter some values and press extract.', style=Pack(padding=10)\n        )\n        self.text_label = toga.Label('Ready.', style=Pack(padding=10))\n        self.password_label = toga.Label('Ready.', style=Pack(padding=10))\n        self.password_content_label = toga.Label(\n            EMPTY_PASSWORD, style=Pack(padding_bottom=10, font_size=9)\n        )\n        self.number_label = toga.Label('Ready.', style=Pack(padding=10))\n\n        # Text inputs and a button\n        self.text_input = toga.TextInput(\n            value='Initial value',\n            placeholder='Type something...', style=Pack(padding=10)\n        )\n        self.text_input_placeholder = toga.TextInput(\n            placeholder='Type something...', style=Pack(padding=10)\n        )\n        self.password_input = toga.PasswordInput(\n            placeholder='Password...',\n            style=Pack(padding=10),\n            on_change=self.on_password_change,\n            validators=[\n                validators.MinLength(10),\n                validators.ContainsUppercase(),\n                validators.ContainsLowercase(),\n                validators.ContainsSpecial(),\n                validators.ContainsDigit()\n            ]\n        )\n        self.email_input = toga.TextInput(\n            placeholder='Email...',\n            style=Pack(padding=10),\n            validators=[validators.Email()]\n        )\n        self.number_input = toga.NumberInput(style=Pack(padding=10))\n        btn_extract = toga.Button(\n            'Extract values',\n            on_press=self.do_extract_values,\n            style=Pack(flex=1),\n        )\n\n        # Outermost box\n        box = toga.Box(\n            children=[\n                self.label,\n                self.text_input,\n                self.text_input_placeholder,\n                self.password_input,\n                self.password_content_label,\n                self.email_input,\n                self.number_input,\n                self.text_label,\n                self.password_label,\n                self.number_label,\n                btn_extract,\n            ],\n            style=Pack(\n                flex=1,\n                direction=COLUMN,\n                padding=10,\n            )\n        )\n\n        # Add the content on the main window\n        self.main_window.content = box\n\n        # Show the main window\n        self.main_window.show()\n\n    def on_password_change(self, widget):\n        content = widget.value\n        self.password_content_label.text = self.get_password_content_label(content)\n\n    def get_password_content_label(self, content):\n        if content.strip() == \"\":\n            return EMPTY_PASSWORD\n        contains = set()\n        for letter in content:\n            if letter in ascii_uppercase:\n                contains.add(\"uppercase letters\")\n            elif letter in ascii_lowercase:\n                contains.add(\"lowercase letters\")\n            elif letter in digits:\n                contains.add(\"digits\")\n            else:\n                contains.add(\"special characters\")\n        return \"Password contains: {}\".format(', '.join(contains))\n\n\ndef main():\n    return TextInputApp('TextInput', 'org.beeware.widgets.textinput')\n\n\nif __name__ == '__main__':\n    app = main()\n    app.main_loop()\n", "path": "examples/textinput/textinput/app.py"}, {"content": "import warnings\n\nfrom toga.handlers import wrapped_handler\n\nfrom .base import Widget\n\n\nclass TextInput(Widget):\n    \"\"\" A widget get user input.\n\n    Args:\n        id (str): An identifier for this widget.\n        style (:obj:`Style`): An optional style object. If no style is provided then\n            a new one will be created for the widget.\n        factory (:obj:`module`): A python module that is capable to return a\n            implementation of this class with the same name. (optional & normally not needed)\n        value (str): The initial text for the input.\n        placeholder (str): If no input is present this text is shown.\n        readonly (bool):  Whether a user can write into the text input, defaults to `False`.\n        on_change (Callable): Method to be called when text is changed in text box\n        validators (list): list of validators to run on the value of the text box. Should\n            return None is value is valid and an error message if not.\n        on_change (``callable``): The handler to invoke when the text changes.\n        on_gain_focus (:obj:`callable`): Function to execute when get focused.\n        on_lose_focus (:obj:`callable`): Function to execute when lose focus.\n    \"\"\"\n    MIN_WIDTH = 100\n\n    def __init__(\n        self,\n        id=None,\n        style=None,\n        factory=None,\n        value=None,\n        placeholder=None,\n        readonly=False,\n        on_change=None,\n        on_gain_focus=None,\n        on_lose_focus=None,\n        validators=None,\n        initial=None,  # DEPRECATED!\n    ):\n        super().__init__(id=id, style=style, factory=factory)\n\n        # Create a platform specific implementation of the widget\n        self._create()\n\n        ##################################################################\n        # 2022-07: Backwards compatibility\n        ##################################################################\n\n        # initial replaced with value\n        if initial is not None:\n            if value is not None:\n                raise ValueError(\n                    \"Cannot specify both `initial` and `value`; \"\n                    \"`initial` has been deprecated, use `value`\"\n                )\n            else:\n                warnings.warn(\n                    \"`initial` has been renamed `value`\", DeprecationWarning\n                )\n            value = initial\n\n        ##################################################################\n        # End backwards compatibility.\n        ##################################################################\n\n        self.placeholder = placeholder\n        self.readonly = readonly\n\n        # Set the actual value before on_change, because we do not want on_change triggered by it\n        # However, we need to prime the handler property in case it is accessed.\n        self._on_change = None\n        self.value = value\n\n        self.on_change = on_change\n        self.validators = validators\n        self.on_lose_focus = on_lose_focus\n        self.on_gain_focus = on_gain_focus\n\n    def _create(self):\n        self._impl = self.factory.TextInput(interface=self)\n\n    @property\n    def readonly(self):\n        \"\"\" Whether a user can write into the text input\n\n        Returns:\n            ``True`` if only read is possible.\n            ``False`` if read and write is possible.\n        \"\"\"\n        return self._readonly\n\n    @readonly.setter\n    def readonly(self, value):\n        self._readonly = value\n        self._impl.set_readonly(value)\n\n    @property\n    def placeholder(self):\n        \"\"\" The placeholder text.\n\n        Returns:\n            The placeholder text as a ``str``.\n        \"\"\"\n        return self._placeholder\n\n    @placeholder.setter\n    def placeholder(self, value):\n        if value is None:\n            self._placeholder = ''\n        else:\n            self._placeholder = str(value)\n        self._impl.set_placeholder(value)\n\n    @property\n    def value(self):\n        \"\"\" The value of the text input field\n\n        Returns:\n            The current text of the widget as a ``str``.\n        \"\"\"\n        return self._impl.get_value()\n\n    @value.setter\n    def value(self, value):\n        if value is None:\n            v = ''\n        else:\n            v = str(value)\n        self._impl.set_value(v)\n\n    @property\n    def is_valid(self):\n        return self._impl.is_valid()\n\n    def clear(self):\n        \"\"\" Clears the text of the widget \"\"\"\n        self.value = ''\n\n    @property\n    def on_change(self):\n        \"\"\"The handler to invoke when the value changes\n\n        Returns:\n            The function ``callable`` that is called on a content change.\n        \"\"\"\n        return self._on_change\n\n    @on_change.setter\n    def on_change(self, handler):\n        \"\"\"Set the handler to invoke when the value is changed.\n\n        Args:\n            handler (:obj:`callable`): The handler to invoke when the value is changed.\n        \"\"\"\n        self._on_change = wrapped_handler(self, handler)\n        self._impl.set_on_change(self._on_change)\n\n    @property\n    def validators(self):\n        return self._validators\n\n    @validators.setter\n    def validators(self, validators):\n        if validators is None:\n            self._validators = []\n        else:\n            self._validators = validators\n        self.validate()\n\n    @property\n    def on_gain_focus(self):\n        \"\"\"The handler to invoke when the widget get focus.\n\n        Returns:\n            The function ``callable`` that is called on widget focus gain.\n        \"\"\"\n        return self._on_gain_focus\n\n    @on_gain_focus.setter\n    def on_gain_focus(self, handler):\n        self._on_gain_focus = wrapped_handler(self, handler)\n        self._impl.set_on_gain_focus(self._on_gain_focus)\n\n    @property\n    def on_lose_focus(self):\n        \"\"\"The handler to invoke when the widget lose focus.\n\n        Returns:\n            The function ``callable`` that is called on widget focus loss.\n        \"\"\"\n        return self._on_lose_focus\n\n    @on_lose_focus.setter\n    def on_lose_focus(self, handler):\n        self._on_lose_focus = wrapped_handler(self, handler)\n        self._impl.set_on_lose_focus(self._on_lose_focus)\n\n    def validate(self):\n        error_message = None\n        for validator in self.validators:\n            if error_message is None:\n                error_message = validator(self.value)\n\n        if error_message is None:\n            self._impl.clear_error()\n            return True\n        else:\n            self._impl.set_error(error_message)\n            return False\n", "path": "src/core/toga/widgets/textinput.py"}]}