MultiLineTextInput widget persistently takes on rich formatting on a paste operation
Describe the bug
When pasting rich-formatted text into a MultiLineTextInput widget, the widget takes on the rich formatting.
Steps to reproduce
- Launch a Toga application that includes a MultiLineTextInput widget. e.g., from the Toga repository, examples/multilinetextinput.
- Copy rich-formatted text from another application, e.g., MS Word, TextEdit, a web browser.
- Paste into the MultiLineTextInput widget.
- Rich-formatting is preserved in the MultiLineTextInput widget. The formatting persists even when clearing the value (e.g., "clear" button in the Toga multilinetextinput example).
Expected behavior
While rich formatting may be desirable in some situations, I'd expect the default behavior to be displaying only plain text, like a TextInput widget.
Screenshots
Before paste:
Rich text (TextEdit):
After paste:
After clear:
Environment
-
Operating System:
- MacOS Sequoia 15.1
- Windows 11
- Others???
-
Python version:
- 3.12.7
-
Software versions:
- Toga: 0.4.8
Logs
Additional context
No response
Confirming this definitely isn't the intended behavior. I'm guessing we've missed an option when configuring the native text widgets.
Not sure if this also affects GTK, iOS or Android; seems highly plausible it might be an issue on GTK.
As least so far as MacOS goes, this seems to prevent MultiLineTextInput from taking rich-formatted text on a paste:
diff --git a/cocoa/src/toga_cocoa/widgets/multilinetextinput.py b/cocoa/src/toga_cocoa/widgets/multilinetextinput.py
index c651fa8e4..e908ecb6d 100644
--- a/cocoa/src/toga_cocoa/widgets/multilinetextinput.py
+++ b/cocoa/src/toga_cocoa/widgets/multilinetextinput.py
@@ -36,6 +36,7 @@ class MultilineTextInput(Widget):
# Create the actual text widget
self.native_text = TogaTextView.alloc().init()
+ self.native_text.setRichText_(False)
self.native_text.interface = self.interface
self.native_text.delegate = self.native_text
See https://developer.apple.com/documentation/appkit/nstextview/1449538-richtext.
For Windows, it doesn't seem like you can disable rich text in the Winforms RichTextBox entirely, but intercepting and handling a CTRL-V to only take the plain text seems to work:
diff --git a/winforms/src/toga_winforms/widgets/multilinetextinput.py b/winforms/src/toga_winforms/widgets/multilinetextinput.py
index 7111d47c1..30667dc82 100644
--- a/winforms/src/toga_winforms/widgets/multilinetextinput.py
+++ b/winforms/src/toga_winforms/widgets/multilinetextinput.py
@@ -22,11 +22,20 @@ class MultilineTextInput(TextInput):
self.native.GotFocus += WeakrefCallable(self.winforms_got_focus)
self.native.LostFocus += WeakrefCallable(self.winforms_lost_focus)
+ # Handle paste (CTRL+V) events
+ self.native.KeyDown += self.handle_paste
+
# Dummy values used during initialization
self._placeholder = ""
self._placeholder_visible = True
self.set_color(None)
+ def handle_paste(self, sender, event):
+ if event.Control and event.KeyCode == WinForms.Keys.V:
+ event.SuppressKeyPress = True
+ if WinForms.Clipboard.ContainsText():
+ self.native.SelectedText = WinForms.Clipboard.GetText(WinForms.TextDataFormat.Text)
+
def winforms_got_focus(self, sender, event):
# If the placeholder is visible when we gain focus, the widget is empty;
# so make the native text empty and hide the placeholder.
This isn't as general as the MacOS solution in my prior comment because the box still takes rich text, and this only handles placing rich text into the box via CTRL-V. Still, a MultiLineTextInput widget doesn't appear to accept drag-and-drop on Windows, so this may be sufficient.
Another case to consider may be if a user provides a UI-based paste option (e.g., Edit > Paste), so perhaps there is another paste event that would need to be intercepted to cover that.
In my quick testing, this does not appear to be an issue for GTK.
It also does not appear to be an issue for iOS and Android—at least when pasting from macOS into an emulator, I haven't tested it natively.
This isn't as general as the MacOS solution in my prior comment because the box still takes rich text, and this only handles placing rich text into the box via CTRL-V. Still, a MultiLineTextInput widget doesn't appear to accept drag-and-drop on Windows, so this may be sufficient.
Looking at the implementation - the underlying widget is a RichTextBox, which is likely why there aren't any options to disable rich text.
I did find this answer on StackOverflow which suggests a possible method for configuring a RichTextBox by poking the underlying win32 API to disable some mode settings on the widget. If you want to tinker with this approach, TextInput has an existing usage of SendMessage to trigger some internal logic around placeholders.
Another case to consider may be if a user provides a UI-based paste option (e.g., Edit > Paste), so perhaps there is another paste event that would need to be intercepted to cover that.
At least at present, we haven't got a default paste menu item for Windows (although perhaps we should); so this isn't an immediate issue; but agreed that a "generic" solution not directly tied to the keyboard handler would be preferable.
Every text box on Windows has a right-click context menu with a Paste command, which is affected by this issue as well.
A workaround in MacOS is to save a Favourite for a Style from the secondary click menu in the MultiLine box, but you have to repeat the process after every rich paste.