-
-
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
Changes from 18 commits
aad61f9
b44f27d
ae1dfc0
34a22ed
d79bf87
511d602
1b24ce9
d9b7645
2ea9673
332ae41
20eedf4
8a84232
9566658
e605385
0194f68
88552e8
43a4fd9
1293d76
18c1c77
85f6de4
7f47831
c4b9055
8dd7bc0
e6673c0
9cf7bd1
a0fcd30
1c8f0bb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from java import jclass | ||
|
||
from .label import LabelProbe | ||
|
||
|
||
# On Android, a Button is just a TextView with a state-dependent background image. | ||
mhsmith marked this conversation as resolved.
Show resolved
Hide resolved
|
||
class TextInputProbe(LabelProbe): | ||
native_class = jclass("android.widget.EditText") |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
The usage of the deprecated ``set_wmclass`` API by the GTK backend has been removed. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,9 +11,10 @@ | |
class Constraints: | ||
def __init__(self, widget): | ||
""" | ||
A wrapper object storing the constraints required to position a widget | ||
at a precise location in its container. | ||
|
||
Args: | ||
widget (:class: toga-cocoa.Widget): The widget that should be constrained. | ||
:param widget: The Widget implementation to be constrained. | ||
""" | ||
self.widget = widget | ||
self._container = None | ||
|
@@ -24,69 +25,79 @@ def __init__(self, widget): | |
self.left_constraint = None | ||
self.top_constraint = None | ||
|
||
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 commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) 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 commentThe 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 I've added a nocover to the two affected There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
self.height_constraint.release() | ||
if self.left_constraint: | ||
self.left_constraint.release() | ||
if self.top_constraint: | ||
self.top_constraint.release() | ||
|
||
@property | ||
def container(self): | ||
return self._container | ||
|
||
@container.setter | ||
def container(self, value): | ||
if value is None and self.container: | ||
# print("Remove constraints for", self.widget, 'in', self.container) | ||
# print(f"Remove constraints for {self.widget} in {self.container}") | ||
self.container.native.removeConstraint(self.width_constraint) | ||
self.container.native.removeConstraint(self.height_constraint) | ||
self.container.native.removeConstraint(self.left_constraint) | ||
self.container.native.removeConstraint(self.top_constraint) | ||
self._container = value | ||
else: | ||
self._container = value | ||
# 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 commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately not. See beeware/rubicon-objc#148. |
||
self.widget.native, | ||
NSLayoutAttributeLeft, | ||
NSLayoutRelationEqual, | ||
self.container.native, | ||
NSLayoutAttributeLeft, | ||
1.0, | ||
10, # Use a dummy, non-zero value for now | ||
) | ||
).retain() | ||
self.container.native.addConstraint(self.left_constraint) | ||
|
||
self.top_constraint = NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant_( # NOQA:E501 | ||
self.top_constraint = NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant_( # noqa: E501 | ||
self.widget.native, | ||
NSLayoutAttributeTop, | ||
NSLayoutRelationEqual, | ||
self.container.native, | ||
NSLayoutAttributeTop, | ||
1.0, | ||
5, # Use a dummy, non-zero value for now | ||
) | ||
).retain() | ||
self.container.native.addConstraint(self.top_constraint) | ||
|
||
self.width_constraint = NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant_( # NOQA:E501 | ||
self.width_constraint = NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant_( # noqa: E501 | ||
self.widget.native, | ||
NSLayoutAttributeRight, | ||
NSLayoutRelationEqual, | ||
self.widget.native, | ||
NSLayoutAttributeLeft, | ||
1.0, | ||
50, # Use a dummy, non-zero value for now | ||
) | ||
).retain() | ||
self.container.native.addConstraint(self.width_constraint) | ||
|
||
self.height_constraint = NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant_( # NOQA:E501 | ||
self.height_constraint = NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant_( # noqa: E501 | ||
self.widget.native, | ||
NSLayoutAttributeBottom, | ||
NSLayoutRelationEqual, | ||
self.widget.native, | ||
NSLayoutAttributeTop, | ||
1.0, | ||
30, # Use a dummy, non-zero value for now | ||
) | ||
).retain() | ||
self.container.native.addConstraint(self.height_constraint) | ||
|
||
def update(self, x, y, width, height): | ||
if self.container: | ||
# print("UPDATE", self.widget, 'in', self.container, 'to', x, y, width, height) | ||
# print(f"UPDATE CONSTRAINTS {self.widget} in {self.container} {width}x{height}@{x},{y}") | ||
self.left_constraint.constant = x | ||
self.top_constraint.constant = y | ||
|
||
|
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.