toga-iOS 0.5.2__tar.gz → 0.5.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. toga_ios-0.5.4/CONTRIBUTING.md +7 -0
  2. toga_ios-0.5.4/PKG-INFO +67 -0
  3. toga_ios-0.5.4/README.md +31 -0
  4. toga_ios-0.5.4/pyproject.toml +134 -0
  5. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/app.py +3 -8
  6. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/colors.py +1 -1
  7. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/constraints.py +34 -23
  8. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/container.py +64 -23
  9. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/factory.py +10 -2
  10. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/fonts.py +9 -1
  11. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/images.py +4 -7
  12. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/libs/core_graphics.py +18 -10
  13. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/libs/webkit.py +14 -1
  14. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/widgets/activityindicator.py +7 -0
  15. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/widgets/base.py +1 -1
  16. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/widgets/canvas.py +233 -159
  17. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/widgets/dateinput.py +10 -2
  18. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/widgets/detailedlist.py +52 -0
  19. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/widgets/optioncontainer.py +4 -0
  20. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/widgets/scrollcontainer.py +14 -1
  21. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/widgets/selection.py +52 -0
  22. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/widgets/timeinput.py +3 -2
  23. toga_ios-0.5.4/src/toga_iOS/widgets/webview.py +297 -0
  24. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/window.py +50 -5
  25. toga_ios-0.5.4/src/toga_iOS.egg-info/PKG-INFO +67 -0
  26. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS.egg-info/SOURCES.txt +2 -1
  27. toga_ios-0.5.4/src/toga_iOS.egg-info/entry_points.txt +43 -0
  28. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS.egg-info/requires.txt +1 -1
  29. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/app.py +4 -2
  30. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/fonts.py +1 -3
  31. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/probe.py +15 -6
  32. toga_ios-0.5.4/tests_backend/widgets/__init__.py +0 -0
  33. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/widgets/activityindicator.py +1 -1
  34. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/widgets/base.py +3 -2
  35. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/widgets/canvas.py +6 -1
  36. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/widgets/detailedlist.py +17 -2
  37. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/widgets/multilinetextinput.py +1 -0
  38. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/widgets/numberinput.py +1 -0
  39. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/widgets/optioncontainer.py +5 -0
  40. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/widgets/properties.py +2 -2
  41. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/widgets/scrollcontainer.py +15 -2
  42. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/widgets/switch.py +6 -0
  43. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/widgets/textinput.py +1 -0
  44. toga_ios-0.5.4/tests_backend/window.py +95 -0
  45. toga_ios-0.5.2/CONTRIBUTING.md +0 -11
  46. toga_ios-0.5.2/PKG-INFO +0 -94
  47. toga_ios-0.5.2/README.rst +0 -58
  48. toga_ios-0.5.2/pyproject.toml +0 -83
  49. toga_ios-0.5.2/src/toga_iOS/widgets/webview.py +0 -143
  50. toga_ios-0.5.2/src/toga_iOS.egg-info/PKG-INFO +0 -94
  51. toga_ios-0.5.2/src/toga_iOS.egg-info/entry_points.txt +0 -2
  52. toga_ios-0.5.2/tests_backend/window.py +0 -75
  53. {toga_ios-0.5.2 → toga_ios-0.5.4}/LICENSE +0 -0
  54. {toga_ios-0.5.2 → toga_ios-0.5.4}/setup.cfg +0 -0
  55. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/__init__.py +0 -0
  56. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/command.py +0 -0
  57. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/dialogs.py +0 -0
  58. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/hardware/__init__.py +0 -0
  59. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/hardware/camera.py +0 -0
  60. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/hardware/location.py +0 -0
  61. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/icons.py +0 -0
  62. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/libs/__init__.py +0 -0
  63. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/libs/av_foundation.py +0 -0
  64. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/libs/core_location.py +0 -0
  65. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/libs/core_text.py +0 -0
  66. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/libs/foundation.py +0 -0
  67. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/libs/mapkit.py +0 -0
  68. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/libs/uikit.py +0 -0
  69. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/paths.py +0 -0
  70. {toga_ios-0.5.2/src/toga_iOS/widgets → toga_ios-0.5.4/src/toga_iOS/resources}/__init__.py +0 -0
  71. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/resources/optioncontainer-tab.png +0 -0
  72. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/resources/toga.icns +0 -0
  73. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/screens.py +0 -0
  74. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/statusicons.py +0 -0
  75. {toga_ios-0.5.2/tests_backend → toga_ios-0.5.4/src/toga_iOS/widgets}/__init__.py +0 -0
  76. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/widgets/box.py +0 -0
  77. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/widgets/button.py +0 -0
  78. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/widgets/divider.py +0 -0
  79. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/widgets/imageview.py +0 -0
  80. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/widgets/label.py +0 -0
  81. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/widgets/mapview.py +0 -0
  82. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/widgets/multilinetextinput.py +0 -0
  83. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/widgets/numberinput.py +0 -0
  84. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/widgets/passwordinput.py +0 -0
  85. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/widgets/progressbar.py +0 -0
  86. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/widgets/slider.py +0 -0
  87. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/widgets/switch.py +0 -0
  88. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS/widgets/textinput.py +0 -0
  89. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS.egg-info/dependency_links.txt +0 -0
  90. {toga_ios-0.5.2 → toga_ios-0.5.4}/src/toga_iOS.egg-info/top_level.txt +0 -0
  91. {toga_ios-0.5.2/tests_backend/hardware → toga_ios-0.5.4/tests_backend}/__init__.py +0 -0
  92. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/dialogs.py +0 -0
  93. {toga_ios-0.5.2/tests_backend/widgets → toga_ios-0.5.4/tests_backend/hardware}/__init__.py +0 -0
  94. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/hardware/camera.py +0 -0
  95. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/hardware/location.py +0 -0
  96. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/icons.py +0 -0
  97. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/images.py +0 -0
  98. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/screens.py +0 -0
  99. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/widgets/box.py +0 -0
  100. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/widgets/button.py +0 -0
  101. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/widgets/dateinput.py +0 -0
  102. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/widgets/divider.py +0 -0
  103. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/widgets/imageview.py +0 -0
  104. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/widgets/label.py +0 -0
  105. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/widgets/mapview.py +0 -0
  106. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/widgets/passwordinput.py +0 -0
  107. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/widgets/progressbar.py +0 -0
  108. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/widgets/selection.py +0 -0
  109. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/widgets/slider.py +0 -0
  110. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/widgets/timeinput.py +0 -0
  111. {toga_ios-0.5.2 → toga_ios-0.5.4}/tests_backend/widgets/webview.py +0 -0
@@ -0,0 +1,7 @@
1
+ # Contributing
2
+
3
+ BeeWare <3's contributions!
4
+
5
+ Please be aware that BeeWare operates under a [Code of Conduct](https://beeware.org/community/behavior/code-of-conduct/).
6
+
7
+ If you'd like to contribute to Toga development, our [contribution guide](https://toga.beeware.org/en/latest/how-to/contribute/) details how to set up a development environment, and other requirements we have as part of our contribution process.
@@ -0,0 +1,67 @@
1
+ Metadata-Version: 2.4
2
+ Name: toga-iOS
3
+ Version: 0.5.4
4
+ Summary: An iOS backend for the Toga widget toolkit.
5
+ Author-email: Russell Keith-Magee <russell@keith-magee.com>
6
+ Maintainer-email: BeeWare Team <team@beeware.org>
7
+ License-Expression: BSD-3-Clause
8
+ Project-URL: Homepage, https://beeware.org/project/projects/libraries/toga/
9
+ Project-URL: Funding, https://beeware.org/contributing/membership/
10
+ Project-URL: Documentation, https://toga.beeware.org/
11
+ Project-URL: Tracker, https://github.com/beeware/toga/issues
12
+ Project-URL: Source, https://github.com/beeware/toga
13
+ Project-URL: Changelog, https://toga.beeware.org/en/stable/background/project/releases
14
+ Keywords: gui,widget,cross-platform,toga,mobile,iOS
15
+ Classifier: Development Status :: 4 - Beta
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
24
+ Classifier: Programming Language :: Python :: 3 :: Only
25
+ Classifier: Topic :: Software Development
26
+ Classifier: Topic :: Software Development :: User Interfaces
27
+ Classifier: Topic :: Software Development :: Widget Sets
28
+ Requires-Python: >=3.10
29
+ Description-Content-Type: text/markdown
30
+ License-File: LICENSE
31
+ Requires-Dist: fonttools<5.0.0,>=4.42.1
32
+ Requires-Dist: rubicon-objc<0.6.0,>=0.5.1
33
+ Requires-Dist: toga-core==0.5.4
34
+ Dynamic: license-file
35
+ Dynamic: requires-dist
36
+
37
+ # toga-iOS
38
+
39
+ <!-- rumdl-disable MD013 -->
40
+ [![Python Versions](https://img.shields.io/pypi/pyversions/toga-ios.svg)](https://pypi.python.org/pypi/toga-ios)
41
+ [![BSD-3-Clause License](https://img.shields.io/pypi/l/toga-ios.svg)](https://github.com/beeware/toga-ios/blob/main/LICENSE)
42
+ [![Project status](https://img.shields.io/pypi/status/toga-ios.svg)](https://pypi.python.org/pypi/toga-ios)
43
+ <!-- rumdl-enable MD013 -->
44
+
45
+ An iOS backend for the [Toga widget toolkit](https://beeware.org/toga).
46
+
47
+ This package isn't much use by itself; it needs to be combined with [the core Toga library](https://pypi.python.org/pypi/toga-core).
48
+
49
+ For platform requirements, see the [iOS platform documentation](https://toga.beeware.org/en/latest/reference/platforms/iOS#prerequisites).
50
+
51
+ For more details, see [Toga's documentation](https://toga.beeware.org), or the [Toga project on GitHub](https://github.com/beeware/toga).
52
+
53
+ ## Community
54
+
55
+ Toga is part of the [BeeWare suite](https://beeware.org). You can talk to the community through:
56
+
57
+ - [@beeware@fosstodon.org on Mastodon](https://fosstodon.org/@beeware)
58
+ - [Discord](https://beeware.org/bee/chat/)
59
+ - The Toga [GitHub Discussions forum](https://github.com/beeware/toga/discussions)
60
+
61
+ We foster a welcoming and respectful community as described in our [BeeWare Community Code of Conduct](https://beeware.org/community/behavior/).
62
+
63
+ ## Contributing
64
+
65
+ If you experience problems with Toga, [log them on GitHub](https://github.com/beeware/toga/issues).
66
+
67
+ If you'd like to contribute to Toga development, our [contribution guide](https://toga.beeware.org/en/latest/how-to/contribute/) details how to set up a development environment, and other requirements we have as part of our contribution process.
@@ -0,0 +1,31 @@
1
+ # toga-iOS
2
+
3
+ <!-- rumdl-disable MD013 -->
4
+ [![Python Versions](https://img.shields.io/pypi/pyversions/toga-ios.svg)](https://pypi.python.org/pypi/toga-ios)
5
+ [![BSD-3-Clause License](https://img.shields.io/pypi/l/toga-ios.svg)](https://github.com/beeware/toga-ios/blob/main/LICENSE)
6
+ [![Project status](https://img.shields.io/pypi/status/toga-ios.svg)](https://pypi.python.org/pypi/toga-ios)
7
+ <!-- rumdl-enable MD013 -->
8
+
9
+ An iOS backend for the [Toga widget toolkit](https://beeware.org/toga).
10
+
11
+ This package isn't much use by itself; it needs to be combined with [the core Toga library](https://pypi.python.org/pypi/toga-core).
12
+
13
+ For platform requirements, see the [iOS platform documentation](https://toga.beeware.org/en/latest/reference/platforms/iOS#prerequisites).
14
+
15
+ For more details, see [Toga's documentation](https://toga.beeware.org), or the [Toga project on GitHub](https://github.com/beeware/toga).
16
+
17
+ ## Community
18
+
19
+ Toga is part of the [BeeWare suite](https://beeware.org). You can talk to the community through:
20
+
21
+ - [@beeware@fosstodon.org on Mastodon](https://fosstodon.org/@beeware)
22
+ - [Discord](https://beeware.org/bee/chat/)
23
+ - The Toga [GitHub Discussions forum](https://github.com/beeware/toga/discussions)
24
+
25
+ We foster a welcoming and respectful community as described in our [BeeWare Community Code of Conduct](https://beeware.org/community/behavior/).
26
+
27
+ ## Contributing
28
+
29
+ If you experience problems with Toga, [log them on GitHub](https://github.com/beeware/toga/issues).
30
+
31
+ If you'd like to contribute to Toga development, our [contribution guide](https://toga.beeware.org/en/latest/how-to/contribute/) details how to set up a development environment, and other requirements we have as part of our contribution process.
@@ -0,0 +1,134 @@
1
+ [build-system]
2
+ requires = [
3
+ "setuptools==82.0.1",
4
+ "setuptools_scm==10.0.5",
5
+ "setuptools_dynamic_dependencies==1.0.0",
6
+ ]
7
+ build-backend = "setuptools.build_meta"
8
+
9
+ [project]
10
+ dynamic = ["version", "dependencies"]
11
+ name = "toga-iOS"
12
+ description = "An iOS backend for the Toga widget toolkit."
13
+ readme = "README.md"
14
+ requires-python = ">= 3.10"
15
+ license = "BSD-3-Clause"
16
+ license-files = [
17
+ "LICENSE",
18
+ ]
19
+ authors = [
20
+ {name="Russell Keith-Magee", email="russell@keith-magee.com"},
21
+ ]
22
+ maintainers = [
23
+ {name="BeeWare Team", email="team@beeware.org"},
24
+ ]
25
+ keywords = [
26
+ "gui",
27
+ "widget",
28
+ "cross-platform",
29
+ "toga",
30
+ "mobile",
31
+ "iOS",
32
+ ]
33
+ classifiers = [
34
+ "Development Status :: 4 - Beta",
35
+ "Intended Audience :: Developers",
36
+ "Operating System :: OS Independent",
37
+ "Programming Language :: Python :: 3",
38
+ "Programming Language :: Python :: 3.10",
39
+ "Programming Language :: Python :: 3.11",
40
+ "Programming Language :: Python :: 3.12",
41
+ "Programming Language :: Python :: 3.13",
42
+ "Programming Language :: Python :: 3.14",
43
+ "Programming Language :: Python :: 3 :: Only",
44
+ "Topic :: Software Development",
45
+ "Topic :: Software Development :: User Interfaces",
46
+ "Topic :: Software Development :: Widget Sets",
47
+ ]
48
+
49
+ [project.urls]
50
+ Homepage = "https://beeware.org/project/projects/libraries/toga/"
51
+ Funding = "https://beeware.org/contributing/membership/"
52
+ Documentation = "https://toga.beeware.org/"
53
+ Tracker = "https://github.com/beeware/toga/issues"
54
+ Source = "https://github.com/beeware/toga"
55
+ Changelog = "https://toga.beeware.org/en/stable/background/project/releases"
56
+
57
+ [project.entry-points."toga.backends"]
58
+ iOS = "toga_iOS"
59
+
60
+ [project.entry-points."toga_core.backend.toga_iOS"]
61
+ App = "toga_iOS.app:App"
62
+ Command = "toga_iOS.command:Command"
63
+ Font = "toga_iOS.fonts:Font"
64
+ Icon = "toga_iOS.icons:Icon"
65
+ Image = "toga_iOS.images:Image"
66
+ Paths = "toga_iOS.paths:Paths"
67
+ native_color = "toga_iOS.colors:native_color"
68
+ dialogs = "toga_iOS.dialogs"
69
+ resources = "toga_iOS.resources"
70
+
71
+ # Hardware
72
+ Camera = "toga_iOS.hardware.camera:Camera"
73
+ Location = "toga_iOS.hardware.location:Location"
74
+
75
+ # Status Icons
76
+ MenuStatusIcon = "toga_iOS.statusicons:MenuStatusIcon"
77
+ SimpleStatusIcon = "toga_iOS.statusicons:SimpleStatusIcon"
78
+ StatusIconSet = "toga_iOS.statusicons:StatusIconSet"
79
+
80
+ # Widgets
81
+ ActivityIndicator = "toga_iOS.widgets.activityindicator:ActivityIndicator"
82
+ Box = "toga_iOS.widgets.box:Box"
83
+ Button = "toga_iOS.widgets.button:Button"
84
+ Canvas = "toga_iOS.widgets.canvas:Canvas"
85
+ DateInput = "toga_iOS.widgets.dateinput:DateInput"
86
+ DetailedList = "toga_iOS.widgets.detailedlist:DetailedList"
87
+ Divider = "toga_iOS.widgets.divider:Divider"
88
+ ImageView = "toga_iOS.widgets.imageview:ImageView"
89
+ Label = "toga_iOS.widgets.label:Label"
90
+ MapView = "toga_iOS.widgets.mapview:MapView"
91
+ MultilineTextInput = "toga_iOS.widgets.multilinetextinput:MultilineTextInput"
92
+ NumberInput = "toga_iOS.widgets.numberinput:NumberInput"
93
+ OptionContainer = "toga_iOS.widgets.optioncontainer:OptionContainer"
94
+ PasswordInput = "toga_iOS.widgets.passwordinput:PasswordInput"
95
+ ProgressBar = "toga_iOS.widgets.progressbar:ProgressBar"
96
+ ScrollContainer = "toga_iOS.widgets.scrollcontainer:ScrollContainer"
97
+ # SplitContainer = "toga_iOS.widgets.splitcontainer:SplitContainer"
98
+ Selection = "toga_iOS.widgets.selection:Selection"
99
+ Slider = "toga_iOS.widgets.slider:Slider"
100
+ Switch = "toga_iOS.widgets.switch:Switch"
101
+ Table = "toga_iOS.widgets.table:Table"
102
+ # Tree = "toga_iOS.widgets.tree:Tree"
103
+ TextInput = "toga_iOS.widgets.textinput:TextInput"
104
+ TimeInput = "toga_iOS.widgets.timeinput:TimeInput"
105
+ WebView = "toga_iOS.widgets.webview:WebView"
106
+
107
+ # Windows
108
+ MainWindow = "toga_iOS.window:MainWindow"
109
+ Window = "toga_iOS.window:Window"
110
+
111
+ [tool.setuptools_scm]
112
+ root = ".."
113
+
114
+ [tool.setuptools_dynamic_dependencies]
115
+ dependencies = [
116
+ "fonttools >= 4.42.1, < 5.0.0",
117
+ "rubicon-objc >= 0.5.1, < 0.6.0",
118
+ "toga-core == {version}",
119
+ ]
120
+
121
+ [tool.coverage.run]
122
+ parallel = true
123
+ branch = true
124
+ relative_files = true
125
+
126
+ # See notes in the root pyproject.toml file.
127
+ source = ["src"]
128
+ source_pkgs = ["toga_iOS"]
129
+
130
+ [tool.coverage.paths]
131
+ source = [
132
+ "src/toga_iOS",
133
+ "**/toga_iOS",
134
+ ]
@@ -42,14 +42,6 @@ class PythonAppDelegate(UIResponder):
42
42
  def applicationWillTerminate_(self, application) -> None:
43
43
  print("App about to Terminate.")
44
44
 
45
- @objc_method
46
- def application_didChangeStatusBarOrientation_(
47
- self, application, oldStatusBarOrientation: int
48
- ) -> None:
49
- """This callback is invoked when rotating the device from landscape to portrait
50
- and vice versa."""
51
- App.app.interface.main_window.content.refresh()
52
-
53
45
 
54
46
  class App:
55
47
  # iOS apps exit when the last window is closed
@@ -61,12 +53,15 @@ class App:
61
53
  def __init__(self, interface):
62
54
  self.interface = interface
63
55
  self.interface._impl = self
56
+
64
57
  # Native instance doesn't exist until the lifecycle completes.
65
58
  self.native = None
66
59
 
67
60
  # Add a reference for the PythonAppDelegate class to use.
68
61
  App.app = self
69
62
 
63
+ self._exiting_presentation = False
64
+
70
65
  self.loop = RubiconEventLoop()
71
66
 
72
67
  def create(self):
@@ -12,7 +12,7 @@ def native_color(c):
12
12
  color = CACHE[c]
13
13
  except KeyError:
14
14
  color = UIColor.colorWithRed(
15
- c.rgba.r / 255, green=c.rgba.g / 255, blue=c.rgba.b / 255, alpha=c.rgba.a
15
+ c.rgb.r / 255, green=c.rgb.g / 255, blue=c.rgb.b / 255, alpha=c.rgb.a
16
16
  )
17
17
  CACHE[c] = color
18
18
 
@@ -26,6 +26,8 @@ class Constraints:
26
26
  self.left_constraint = None
27
27
  self.top_constraint = None
28
28
 
29
+ self.constraints_created = False
30
+
29
31
  # Deletion isn't an event we can programmatically invoke; deletion
30
32
  # of constraints can take several iterations before it occurs.
31
33
  def __del__(self): # pragma: nocover
@@ -38,28 +40,46 @@ class Constraints:
38
40
  # the native object of the window's container to be deleted on the ObjC
39
41
  # side before the constraints for the window have been removed. Protect
40
42
  # against this possibility.
41
- if self.container.native:
43
+ # Also protect against the possibility that the constraints have
44
+ # already been cleared.
45
+ if self.container.native and self.constraints_created:
42
46
  self.container.native.removeConstraint(self.width_constraint)
43
47
  self.container.native.removeConstraint(self.height_constraint)
44
48
  self.container.native.removeConstraint(self.left_constraint)
45
49
  self.container.native.removeConstraint(self.top_constraint)
46
50
 
51
+ self.constraints_created = False
52
+
47
53
  @property
48
54
  def container(self):
49
55
  return self._container
50
56
 
51
57
  @container.setter
52
58
  def container(self, value):
53
- # This will *always* remove and then add constraints. It relies on the base
54
- # widget to *not* invoke this setter unless the container is actually changing.
55
-
59
+ # This will invalidate our created constraints, as the container would've
60
+ # changed. Constraints are not created until the actual first layout
61
+ # update, as we do not want the native layer performing any layout passes
62
+ # with wrong initial dummy constrained values.
56
63
  self._remove_constraints()
57
64
  self._container = value
58
- if value is not None:
59
- # print(
60
- # f"Add constraints for {self.widget} in {self.container}"
61
- # f"{self.widget.interface.layout}"
62
- # )
65
+
66
+ def update(self, x, y, width, height):
67
+ # print(
68
+ # f"UPDATE CONSTRAINTS {self.widget} in {self.container} "
69
+ # f"{width}x{height}@{x},{y}"
70
+ # )
71
+ y += self.container.top_inset
72
+ x += self.container.left_inset
73
+
74
+ if self.constraints_created:
75
+ # We already have constraints set up; reuse them.
76
+ self.left_constraint.constant = x
77
+ self.top_constraint.constant = y
78
+
79
+ self.width_constraint.constant = width
80
+ self.height_constraint.constant = height
81
+
82
+ else:
63
83
  self.left_constraint = NSLayoutConstraint.constraintWithItem(
64
84
  self.widget.native,
65
85
  attribute__1=NSLayoutAttributeLeft,
@@ -67,7 +87,7 @@ class Constraints:
67
87
  toItem=self.container.native,
68
88
  attribute__2=NSLayoutAttributeLeft,
69
89
  multiplier=1.0,
70
- constant=10, # Use a dummy, non-zero value for now
90
+ constant=x,
71
91
  )
72
92
  self.container.native.addConstraint(self.left_constraint)
73
93
 
@@ -78,7 +98,7 @@ class Constraints:
78
98
  toItem=self.container.native,
79
99
  attribute__2=NSLayoutAttributeTop,
80
100
  multiplier=1.0,
81
- constant=5, # Use a dummy, non-zero value for now
101
+ constant=y,
82
102
  )
83
103
  self.container.native.addConstraint(self.top_constraint)
84
104
 
@@ -89,7 +109,7 @@ class Constraints:
89
109
  toItem=self.widget.native,
90
110
  attribute__2=NSLayoutAttributeLeft,
91
111
  multiplier=1.0,
92
- constant=50, # Use a dummy, non-zero value for now
112
+ constant=width,
93
113
  )
94
114
  self.container.native.addConstraint(self.width_constraint)
95
115
 
@@ -100,17 +120,8 @@ class Constraints:
100
120
  toItem=self.widget.native,
101
121
  attribute__2=NSLayoutAttributeTop,
102
122
  multiplier=1.0,
103
- constant=30, # Use a dummy, non-zero value for now
123
+ constant=height,
104
124
  )
105
125
  self.container.native.addConstraint(self.height_constraint)
106
126
 
107
- def update(self, x, y, width, height):
108
- # print(
109
- # f"UPDATE CONSTRAINTS {self.widget} in {self.container} "
110
- # f"{width}x{height}@{x},{y}"
111
- # )
112
- self.left_constraint.constant = x
113
- self.top_constraint.constant = y
114
-
115
- self.width_constraint.constant = width
116
- self.height_constraint.constant = height
127
+ self.constraints_created = True
@@ -1,5 +1,6 @@
1
+ from rubicon.objc import objc_method, objc_property, send_super
2
+
1
3
  from .libs import (
2
- UIApplication,
3
4
  UINavigationController,
4
5
  UIView,
5
6
  UIViewAutoresizing,
@@ -15,16 +16,36 @@ from .libs import (
15
16
  #######################################################################################
16
17
 
17
18
 
19
+ class TogaContainerView(UIView):
20
+ container = objc_property(object, weak=True)
21
+
22
+ @objc_method
23
+ def layoutSubviews(self):
24
+ send_super(__class__, self, "layoutSubviews")
25
+ if self.container.on_native_layout:
26
+ self.container.on_native_layout(self.container)
27
+
28
+ @objc_method
29
+ def safeAreaInsetsDidChange(self):
30
+ send_super(__class__, self, "safeAreaInsetsDidChange")
31
+ if self.container.on_native_layout:
32
+ self.container.on_native_layout(self.container)
33
+
34
+
18
35
  class BaseContainer:
19
- def __init__(self, content=None, on_refresh=None):
36
+ def __init__(self, content=None, on_refresh=None, on_native_layout=None):
20
37
  """A base class for iOS containers.
21
38
 
22
39
  :param content: The widget impl that is the container's initial content.
23
40
  :param on_refresh: The callback to be notified when this container's layout is
24
41
  refreshed.
42
+ :param on_native_layout: The callback to be notified when the container's native
43
+ widget has finished laying out, i.e. when native values such as size
44
+ *may* have changed.
25
45
  """
26
46
  self._content = content
27
47
  self.on_refresh = on_refresh
48
+ self.on_native_layout = on_native_layout
28
49
 
29
50
  @property
30
51
  def content(self):
@@ -52,7 +73,13 @@ class BaseContainer:
52
73
 
53
74
 
54
75
  class Container(BaseContainer):
55
- def __init__(self, content=None, layout_native=None, on_refresh=None):
76
+ def __init__(
77
+ self,
78
+ content=None,
79
+ layout_native=None,
80
+ on_refresh=None,
81
+ on_native_layout=None,
82
+ ):
56
83
  """
57
84
  :param content: The widget impl that is the container's initial content.
58
85
  :param layout_native: The native widget that should be used to provide size
@@ -62,9 +89,17 @@ class Container(BaseContainer):
62
89
  the size can be different.
63
90
  :param on_refresh: The callback to be notified when this container's layout is
64
91
  refreshed.
92
+ :param on_native_layout: The callback to be notified when the container's native
93
+ widget has finished laying out, i.e. when native values such as size
94
+ *may* have changed.
65
95
  """
66
- super().__init__(content=content, on_refresh=on_refresh)
67
- self.native = UIView.alloc().init()
96
+ super().__init__(
97
+ content=content,
98
+ on_refresh=on_refresh,
99
+ on_native_layout=on_native_layout,
100
+ )
101
+ self.native = TogaContainerView.alloc().init()
102
+ self.native.container = self
68
103
  self.native.translatesAutoresizingMaskIntoConstraints = True
69
104
  self.native.autoresizingMask = (
70
105
  UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight
@@ -72,6 +107,11 @@ class Container(BaseContainer):
72
107
 
73
108
  self.layout_native = self.native if layout_native is None else layout_native
74
109
 
110
+ self.top_inset = 0
111
+ self.left_inset = 0
112
+ self.bottom_inset = 0
113
+ self.right_inset = 0
114
+
75
115
  def __del__(self):
76
116
  # Mark the contained native object as explicitly None so that the
77
117
  # constraints know the object has been deleted.
@@ -79,15 +119,13 @@ class Container(BaseContainer):
79
119
 
80
120
  @property
81
121
  def width(self):
82
- return self.layout_native.bounds.size.width
122
+ return self.layout_native.bounds.size.width - self.left_inset - self.right_inset
83
123
 
84
124
  @property
85
125
  def height(self):
86
- return self.layout_native.bounds.size.height - self.top_offset
87
-
88
- @property
89
- def top_offset(self):
90
- return 0
126
+ return (
127
+ self.layout_native.bounds.size.height - self.top_inset - self.bottom_inset
128
+ )
91
129
 
92
130
 
93
131
  class ControlledContainer(Container):
@@ -96,6 +134,7 @@ class ControlledContainer(Container):
96
134
  content=None,
97
135
  layout_native=None,
98
136
  on_refresh=None,
137
+ on_native_layout=None,
99
138
  ):
100
139
  """
101
140
  :param content: The widget impl that is the container's initial content.
@@ -106,11 +145,15 @@ class ControlledContainer(Container):
106
145
  rendered, the source of the size can be different.
107
146
  :param on_refresh: The callback to be notified when this container's layout is
108
147
  refreshed.
148
+ :param on_native_layout: The callback to be notified when the container's native
149
+ widget has finished laying out, i.e. when native values such as size
150
+ *may* have changed.
109
151
  """
110
152
  super().__init__(
111
153
  content=content,
112
154
  layout_native=layout_native,
113
155
  on_refresh=on_refresh,
156
+ on_native_layout=on_native_layout,
114
157
  )
115
158
 
116
159
  # Construct a ViewController that provides a navigation bar, and
@@ -128,6 +171,7 @@ class RootContainer(Container):
128
171
  content=None,
129
172
  layout_native=None,
130
173
  on_refresh=None,
174
+ on_native_layout=None,
131
175
  ):
132
176
  """A bare content container.
133
177
 
@@ -141,11 +185,15 @@ class RootContainer(Container):
141
185
  rendered, the source of the size can be different.
142
186
  :param on_refresh: The callback to be notified when this container's layout is
143
187
  refreshed.
188
+ :param on_native_layout: The callback to be notified when the container's native
189
+ widget has finished laying out, i.e. when native values such as size
190
+ *may* have changed.
144
191
  """
145
192
  super().__init__(
146
193
  content=content,
147
194
  layout_native=layout_native,
148
195
  on_refresh=on_refresh,
196
+ on_native_layout=on_native_layout,
149
197
  )
150
198
 
151
199
  # Construct a UIViewController to hold the root content
@@ -154,11 +202,6 @@ class RootContainer(Container):
154
202
  # Set the controller's view to be the root content widget
155
203
  self.controller.view = self.native
156
204
 
157
- # The testbed app won't instantiate a simple app, so we can't test these properties
158
- @property
159
- def top_offset(self): # pragma: no cover
160
- return UIApplication.sharedApplication.statusBarFrame.size.height
161
-
162
205
  @property
163
206
  def title(self): # pragma: no cover
164
207
  return self._title
@@ -174,6 +217,7 @@ class NavigationContainer(Container):
174
217
  content=None,
175
218
  layout_native=None,
176
219
  on_refresh=None,
220
+ on_native_layout=None,
177
221
  ):
178
222
  """A top level container that provides a navigation/title bar.
179
223
 
@@ -185,11 +229,15 @@ class NavigationContainer(Container):
185
229
  rendered, the source of the size can be different.
186
230
  :param on_refresh: The callback to be notified when this container's layout is
187
231
  refreshed.
232
+ :param on_native_layout: The callback to be notified when the container's native
233
+ widget has finished laying out, i.e. when native values such as size
234
+ *may* have changed.
188
235
  """
189
236
  super().__init__(
190
237
  content=content,
191
238
  layout_native=layout_native,
192
239
  on_refresh=on_refresh,
240
+ on_native_layout=on_native_layout,
193
241
  )
194
242
 
195
243
  # Construct a NavigationController that provides a navigation bar, and
@@ -203,13 +251,6 @@ class NavigationContainer(Container):
203
251
  # Set the controller's view to be the root content widget
204
252
  self.content_controller.view = self.native
205
253
 
206
- @property
207
- def top_offset(self):
208
- return (
209
- UIApplication.sharedApplication.statusBarFrame.size.height
210
- + self.controller.navigationBar.frame.size.height
211
- )
212
-
213
254
  @property
214
255
  def title(self):
215
256
  return self.controller.topViewController.title
@@ -1,3 +1,5 @@
1
+ import warnings
2
+
1
3
  from toga import NotImplementedWarning
2
4
 
3
5
  from . import dialogs
@@ -43,8 +45,14 @@ from .widgets.timeinput import TimeInput
43
45
  from .widgets.webview import WebView
44
46
  from .window import MainWindow, Window
45
47
 
48
+ warnings.warn(
49
+ "Factory modules are deprecated. Use 'toga.platform.get_factory' instead.",
50
+ DeprecationWarning,
51
+ stacklevel=1,
52
+ )
53
+
46
54
 
47
- def not_implemented(feature):
55
+ def not_implemented(feature): # pragma: no cover
48
56
  NotImplementedWarning.warn("iOS", feature)
49
57
 
50
58
 
@@ -98,5 +106,5 @@ __all__ = [
98
106
  ]
99
107
 
100
108
 
101
- def __getattr__(name): # pragma: no cover
109
+ def __getattr__(name):
102
110
  raise NotImplementedError(f"Toga's iOS backend doesn't implement {name}")
@@ -97,7 +97,15 @@ class Font:
97
97
 
98
98
  def load_arbitrary_system_font(self):
99
99
  """Use a font available on the system."""
100
- raise UnknownFontError("Arbitrary system fonts not yet supported on iOS")
100
+ self._assign_native(self.interface.family)
101
+ # Fonts *can* fail safe - creating a font object where the family doesn't match
102
+ # the requested name. If a font wasn't loaded, or the loaded font name doesn't
103
+ # match the font request, assume the font wasn't found.
104
+ if self.native is None or self.native.fontName != self.interface.family:
105
+ # If it wasn't a match, purge the font cache of the loaded font
106
+ self.native = None
107
+ del _IMPL_CACHE[self.interface]
108
+ raise UnknownFontError(f"Unknown system font: {self.interface.family}")
101
109
 
102
110
  def _assign_native(self, font_name):
103
111
  if self.interface.size == SYSTEM_DEFAULT_FONT_SIZE:
@@ -1,6 +1,7 @@
1
1
  from ctypes import POINTER, c_char, cast
2
2
  from pathlib import Path
3
3
 
4
+ from toga.images import ImageLoadError
4
5
  from toga_iOS.libs import (
5
6
  NSData,
6
7
  UIImage,
@@ -20,19 +21,15 @@ def nsdata_to_bytes(data: NSData) -> bytes:
20
21
  class Image:
21
22
  RAW_TYPE = UIImage
22
23
 
23
- def __init__(self, interface, path=None, data=None, raw=None):
24
+ def __init__(self, interface, data=None, raw=None):
24
25
  self.interface = interface
25
26
 
26
- if path:
27
- self.native = UIImage.imageWithContentsOfFile(str(path))
28
- if self.native is None:
29
- raise ValueError(f"Unable to load image from {path}")
30
- elif data:
27
+ if data:
31
28
  self.native = UIImage.imageWithData(
32
29
  NSData.dataWithBytes(data, length=len(data))
33
30
  )
34
31
  if self.native is None:
35
- raise ValueError("Unable to load image from data")
32
+ raise ImageLoadError
36
33
  else:
37
34
  self.native = raw
38
35