-
-
Notifications
You must be signed in to change notification settings - Fork 674
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[widget audit] toga.Widget #1834
Conversation
f6f72cd
to
d47445d
Compare
…odes with no applicator.
if not view.getClass().isInstance(self.native): | ||
# save guard for Widgets like Canvas that are not based on View | ||
self.interface.factory.not_implemented("Widget.set_hidden()") | ||
return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure why this was done as an isinstance check, rather than by overriding in the subclass, but I've moved it.
# but it's the only way I've found that actually sets the | ||
# Application name to something other than '__main__.py'. | ||
self.native.set_wmclass(app.interface.name, app.interface.name) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This keeps getting reported as a bug, even though it's clearly labeled a warning; since it's functionally broken on Ubuntu 22.04 (although it still works on Fedora 36) anyway, it seems better to remove the deprecated call and find a different approach.
@@ -15,7 +15,7 @@ class DatePicker(Widget): | |||
a new one will be created for the widget. | |||
""" | |||
|
|||
MIN_WIDTH = 200 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably be handled with a rehint based on the preferred size of the widget, rather than a hard coded value - but we can tackle that when we audit DatePicker.
I've reverted my last push and created #1853 instead. |
cocoa/src/toga_cocoa/constraints.py
Outdated
def __del__(self): | ||
if self.width_constraint: | ||
self.width_constraint.release() | ||
if self.height_constraint: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method isn't completely covered by the testbed. And if I understand correctly, a widget can only be associated with one Constraints
object during its lifetime, so I'm not sure why it's necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're correct that a widget can only be associated with a single constraints object - but if a widget is deleted, we need to free the associated constraints object, which in turn means releasing the ObjC instances that have been explicitly retained.
The (missing) retain()
calls weren't causing a problem on macOS, but they were on iOS; however, that's probably more a case of accidentally not hitting the problem, rather than the problem not being there.
Exercising this could be interesting... I'll see what I can do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah - I can't work out a way to reliably get coverage here. The garbage collector of the ObjC runtime and the Python garbage collector both need to agree that a widget has been destroyed before __del__
is invoked on an interface widget; and that then needs to cascade down to the impl, and then the constraints object.
I've added a nocover to the two affected __del__
methods for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case, isn't there a possible leak if a widget is removed from its container and then added to another container? When it's added to the new container, new constraints objects are created without releasing the old ones.
The simplest solution would be to allocate the constraints objects in the constructor and then reuse them for all containers in the widget's lifetime. Or if that isn't possible because they're tied to the container, maybe the content of __del__
could be factored out into another method, and called from the if value is None and self.container
branch of container.setter
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch on the leak when reparenting; I'll fix that case.
Agreed that creating the constraint objects once would be preferable - except that the container is an argument to the top and left constraints. We'd also need to manage adding and removing the constraint from the existing containers. At that point, the accounting associated with destroying and creating fresh constraints isn't that far removed from what we're already doing.
Refactoring the cleanup code into a common location is definitely a good idea though, and it cleans up the setter as well, so I've taken that approach.
# print("Add constraints for", self.widget, 'in', self.container, self.widget.interface.layout) | ||
self.left_constraint = NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant_( # NOQA:E501 | ||
# print(f"Add constraints for {self.widget} in {self.container} {self.widget.interface.layout}) | ||
self.left_constraint = NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant_( # noqa: E501 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could these E501 errors be fixed by using keyword argument syntax instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately not. See beeware/rubicon-objc#148.
37355c9
to
2400e61
Compare
2400e61
to
a0fcd30
Compare
Sorry, looks like I merged some end of line whitespace. I've pushed a fix to #1861. |
An audit of the base widget.
Allows a widget to be assigned to a different app (should never happen in practice, but it makes testing easier)
Simplifies logic needing to verify if a widget has an implementation before it is added as a child. It's effectively impossible to create a widget that doesn't have an
_impl
- or, at least, the one case where it can happen (creating a box with children in the constructor) is trivial to avoid by adding the children after creating the impl.Corrects a bug in Pack that would ignore an explicit width or height on a widget if the widget had children.
Corrects the handling of visibility, removing
visibility = NONE
as a style option (following CSS).Adds a bare stub of a probe for TextInput. This is needed because we need a widget that can accept input focus. On Mobile, "input focus" means "show the keyboard", so we need a widget with keyboard input.
Includes a fix to remove the usage of the deprecated
set_wmclass
API on GTK.The GTK implementation works, but I've had to mark the
has_focus
probe as a skip for now, because it appears that focus isn't being reported correctly in CI. I'm guessing this is because of CI.Coverage on Winforms isn't strictly at 100% - however, of the 4 lines that aren't covered:
Fixes #1718
Audit checklist