toga-iOS 0.4.2__tar.gz → 0.5.0__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.
- {toga-iOS-0.4.2/src/toga_iOS.egg-info → toga_ios-0.5.0}/PKG-INFO +10 -8
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/README.rst +1 -1
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/pyproject.toml +8 -7
- toga_ios-0.5.0/src/toga_iOS/__init__.py +3 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/app.py +68 -24
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/colors.py +1 -2
- toga_ios-0.5.0/src/toga_iOS/command.py +28 -0
- toga_ios-0.5.0/src/toga_iOS/constraints.py +116 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/container.py +53 -5
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/dialogs.py +39 -38
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/factory.py +20 -4
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/fonts.py +1 -1
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/hardware/camera.py +7 -5
- toga_ios-0.5.0/src/toga_iOS/hardware/location.py +146 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/icons.py +5 -8
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/images.py +0 -6
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/libs/__init__.py +2 -0
- toga_ios-0.5.0/src/toga_iOS/libs/core_location.py +45 -0
- toga_ios-0.5.0/src/toga_iOS/libs/mapkit.py +63 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/libs/uikit.py +6 -1
- toga_ios-0.5.0/src/toga_iOS/screens.py +45 -0
- toga_ios-0.5.0/src/toga_iOS/statusicons.py +32 -0
- toga_ios-0.5.0/src/toga_iOS/widgets/activityindicator.py +31 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/widgets/base.py +13 -18
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/widgets/box.py +7 -4
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/widgets/button.py +1 -4
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/widgets/canvas.py +12 -9
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/widgets/detailedlist.py +2 -4
- toga_ios-0.5.0/src/toga_iOS/widgets/divider.py +43 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/widgets/imageview.py +0 -8
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/widgets/label.py +7 -11
- toga_ios-0.5.0/src/toga_iOS/widgets/mapview.py +192 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/widgets/multilinetextinput.py +29 -32
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/widgets/numberinput.py +1 -4
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/widgets/scrollcontainer.py +4 -5
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/widgets/selection.py +8 -5
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/widgets/switch.py +6 -1
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/widgets/textinput.py +24 -29
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/widgets/webview.py +65 -4
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/window.py +94 -39
- {toga-iOS-0.4.2 → toga_ios-0.5.0/src/toga_iOS.egg-info}/PKG-INFO +10 -8
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS.egg-info/SOURCES.txt +14 -0
- toga_ios-0.5.0/src/toga_iOS.egg-info/requires.txt +3 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/app.py +26 -1
- toga_ios-0.5.0/tests_backend/dialogs.py +67 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/hardware/camera.py +9 -4
- toga_ios-0.5.0/tests_backend/hardware/location.py +185 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/icons.py +3 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/probe.py +6 -5
- toga_ios-0.5.0/tests_backend/screens.py +16 -0
- toga_ios-0.5.0/tests_backend/widgets/activityindicator.py +7 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/widgets/base.py +3 -3
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/widgets/detailedlist.py +24 -10
- toga_ios-0.5.0/tests_backend/widgets/divider.py +7 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/widgets/label.py +5 -5
- toga_ios-0.5.0/tests_backend/widgets/mapview.py +102 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/widgets/numberinput.py +4 -4
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/widgets/optioncontainer.py +5 -1
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/widgets/properties.py +1 -1
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/widgets/selection.py +5 -5
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/widgets/switch.py +4 -1
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/widgets/textinput.py +5 -5
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/widgets/webview.py +3 -0
- toga_ios-0.5.0/tests_backend/window.py +75 -0
- toga-iOS-0.4.2/src/toga_iOS/__init__.py +0 -3
- toga-iOS-0.4.2/src/toga_iOS/command.py +0 -7
- toga-iOS-0.4.2/src/toga_iOS/constraints.py +0 -110
- toga-iOS-0.4.2/src/toga_iOS.egg-info/requires.txt +0 -3
- toga-iOS-0.4.2/tests_backend/window.py +0 -79
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/CONTRIBUTING.md +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/LICENSE +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/setup.cfg +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/hardware/__init__.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/libs/av_foundation.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/libs/core_graphics.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/libs/core_text.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/libs/foundation.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/libs/webkit.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/paths.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/resources/optioncontainer-tab.png +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/resources/toga.icns +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/widgets/__init__.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/widgets/optioncontainer.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/widgets/passwordinput.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/widgets/progressbar.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS/widgets/slider.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS.egg-info/dependency_links.txt +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS.egg-info/entry_points.txt +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/src/toga_iOS.egg-info/top_level.txt +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/__init__.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/fonts.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/hardware/__init__.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/images.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/widgets/__init__.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/widgets/box.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/widgets/button.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/widgets/canvas.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/widgets/imageview.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/widgets/multilinetextinput.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/widgets/passwordinput.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/widgets/progressbar.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/widgets/scrollcontainer.py +0 -0
- {toga-iOS-0.4.2 → toga_ios-0.5.0}/tests_backend/widgets/slider.py +0 -0
|
@@ -1,36 +1,38 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: toga-iOS
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: An iOS backend for the Toga widget toolkit.
|
|
5
5
|
Author-email: Russell Keith-Magee <russell@keith-magee.com>
|
|
6
6
|
Maintainer-email: BeeWare Team <team@beeware.org>
|
|
7
7
|
License: New BSD
|
|
8
8
|
Project-URL: Homepage, https://beeware.org/project/projects/libraries/toga/
|
|
9
9
|
Project-URL: Funding, https://beeware.org/contributing/membership/
|
|
10
|
-
Project-URL: Documentation, https://toga.readthedocs.io/
|
|
10
|
+
Project-URL: Documentation, https://toga.readthedocs.io/
|
|
11
11
|
Project-URL: Tracker, https://github.com/beeware/toga/issues
|
|
12
12
|
Project-URL: Source, https://github.com/beeware/toga
|
|
13
|
+
Project-URL: Changelog, https://toga.readthedocs.io/en/stable/background/project/releases.html
|
|
13
14
|
Keywords: gui,widget,cross-platform,toga,mobile,iOS
|
|
14
15
|
Classifier: Development Status :: 4 - Beta
|
|
15
16
|
Classifier: Intended Audience :: Developers
|
|
16
17
|
Classifier: License :: OSI Approved :: BSD License
|
|
17
18
|
Classifier: Operating System :: OS Independent
|
|
18
19
|
Classifier: Programming Language :: Python :: 3
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.9
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.10
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.11
|
|
23
23
|
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
25
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
25
26
|
Classifier: Topic :: Software Development
|
|
26
27
|
Classifier: Topic :: Software Development :: User Interfaces
|
|
27
28
|
Classifier: Topic :: Software Development :: Widget Sets
|
|
28
|
-
Requires-Python: >=3.
|
|
29
|
+
Requires-Python: >=3.9
|
|
29
30
|
Description-Content-Type: text/x-rst
|
|
30
31
|
License-File: LICENSE
|
|
31
32
|
Requires-Dist: fonttools<5.0.0,>=4.42.1
|
|
32
|
-
Requires-Dist: rubicon-objc<0.
|
|
33
|
-
Requires-Dist: toga-core==0.
|
|
33
|
+
Requires-Dist: rubicon-objc<0.6.0,>=0.5.0
|
|
34
|
+
Requires-Dist: toga-core==0.5.0
|
|
35
|
+
Dynamic: requires-dist
|
|
34
36
|
|
|
35
37
|
toga-iOS
|
|
36
38
|
========
|
|
@@ -40,7 +42,7 @@ An iOS backend for the `Toga widget toolkit`_.
|
|
|
40
42
|
This package isn't much use by itself; it needs to be combined with `the core Toga library`_.
|
|
41
43
|
|
|
42
44
|
For platform requirements, see the `iOS platform documentation
|
|
43
|
-
<https://toga.readthedocs.io/en/
|
|
45
|
+
<https://toga.readthedocs.io/en/latest/reference/platforms/iOS.html#prerequisites>`__.
|
|
44
46
|
|
|
45
47
|
For more details, see the `Toga project on Github`_.
|
|
46
48
|
|
|
@@ -6,7 +6,7 @@ An iOS backend for the `Toga widget toolkit`_.
|
|
|
6
6
|
This package isn't much use by itself; it needs to be combined with `the core Toga library`_.
|
|
7
7
|
|
|
8
8
|
For platform requirements, see the `iOS platform documentation
|
|
9
|
-
<https://toga.readthedocs.io/en/
|
|
9
|
+
<https://toga.readthedocs.io/en/latest/reference/platforms/iOS.html#prerequisites>`__.
|
|
10
10
|
|
|
11
11
|
For more details, see the `Toga project on Github`_.
|
|
12
12
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
[build-system]
|
|
2
2
|
requires = [
|
|
3
|
-
"setuptools==
|
|
4
|
-
"setuptools_scm==8.0
|
|
5
|
-
"setuptools_dynamic_dependencies
|
|
3
|
+
"setuptools==76.0.0",
|
|
4
|
+
"setuptools_scm==8.2.0",
|
|
5
|
+
"setuptools_dynamic_dependencies==1.0.0",
|
|
6
6
|
]
|
|
7
7
|
build-backend = "setuptools.build_meta"
|
|
8
8
|
|
|
@@ -11,7 +11,7 @@ dynamic = ["version", "dependencies"]
|
|
|
11
11
|
name = "toga-iOS"
|
|
12
12
|
description = "An iOS backend for the Toga widget toolkit."
|
|
13
13
|
readme = "README.rst"
|
|
14
|
-
requires-python = ">= 3.
|
|
14
|
+
requires-python = ">= 3.9"
|
|
15
15
|
license.text = "New BSD"
|
|
16
16
|
authors = [
|
|
17
17
|
{name="Russell Keith-Magee", email="russell@keith-magee.com"},
|
|
@@ -33,11 +33,11 @@ classifiers = [
|
|
|
33
33
|
"License :: OSI Approved :: BSD License",
|
|
34
34
|
"Operating System :: OS Independent",
|
|
35
35
|
"Programming Language :: Python :: 3",
|
|
36
|
-
"Programming Language :: Python :: 3.8",
|
|
37
36
|
"Programming Language :: Python :: 3.9",
|
|
38
37
|
"Programming Language :: Python :: 3.10",
|
|
39
38
|
"Programming Language :: Python :: 3.11",
|
|
40
39
|
"Programming Language :: Python :: 3.12",
|
|
40
|
+
"Programming Language :: Python :: 3.13",
|
|
41
41
|
"Programming Language :: Python :: 3 :: Only",
|
|
42
42
|
"Topic :: Software Development",
|
|
43
43
|
"Topic :: Software Development :: User Interfaces",
|
|
@@ -47,9 +47,10 @@ classifiers = [
|
|
|
47
47
|
[project.urls]
|
|
48
48
|
Homepage = "https://beeware.org/project/projects/libraries/toga/"
|
|
49
49
|
Funding = "https://beeware.org/contributing/membership/"
|
|
50
|
-
Documentation = "https://toga.readthedocs.io/
|
|
50
|
+
Documentation = "https://toga.readthedocs.io/"
|
|
51
51
|
Tracker = "https://github.com/beeware/toga/issues"
|
|
52
52
|
Source = "https://github.com/beeware/toga"
|
|
53
|
+
Changelog = "https://toga.readthedocs.io/en/stable/background/project/releases.html"
|
|
53
54
|
|
|
54
55
|
[project.entry-points."toga.backends"]
|
|
55
56
|
iOS = "toga_iOS"
|
|
@@ -60,7 +61,7 @@ root = ".."
|
|
|
60
61
|
[tool.setuptools_dynamic_dependencies]
|
|
61
62
|
dependencies = [
|
|
62
63
|
"fonttools >= 4.42.1, < 5.0.0",
|
|
63
|
-
"rubicon-objc >= 0.
|
|
64
|
+
"rubicon-objc >= 0.5.0, < 0.6.0",
|
|
64
65
|
"toga-core == {version}",
|
|
65
66
|
]
|
|
66
67
|
|
|
@@ -3,30 +3,32 @@ import asyncio
|
|
|
3
3
|
from rubicon.objc import objc_method
|
|
4
4
|
from rubicon.objc.eventloop import EventLoopPolicy, iOSLifecycle
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
from toga_iOS.
|
|
6
|
+
import toga
|
|
7
|
+
from toga_iOS.libs import UIResponder, UIScreen, av_foundation
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
class MainWindow(Window):
|
|
11
|
-
_is_main_window = True
|
|
9
|
+
from .screens import Screen as ScreenImpl
|
|
12
10
|
|
|
13
11
|
|
|
14
12
|
class PythonAppDelegate(UIResponder):
|
|
15
13
|
@objc_method
|
|
16
14
|
def applicationDidBecomeActive_(self, application) -> None:
|
|
17
15
|
print("App became active.")
|
|
16
|
+
App.app.interface.current_window.on_gain_focus()
|
|
18
17
|
|
|
19
18
|
@objc_method
|
|
20
19
|
def applicationWillResignActive_(self, application) -> None:
|
|
21
20
|
print("App about to leave foreground.", flush=True)
|
|
21
|
+
App.app.interface.current_window.on_lose_focus()
|
|
22
22
|
|
|
23
23
|
@objc_method
|
|
24
24
|
def applicationDidEnterBackground_(self, application) -> None:
|
|
25
25
|
print("App entered background.")
|
|
26
|
+
App.app.interface.current_window.on_hide()
|
|
26
27
|
|
|
27
28
|
@objc_method
|
|
28
29
|
def applicationWillEnterForeground_(self, application) -> None:
|
|
29
30
|
print("App about to enter foreground.")
|
|
31
|
+
App.app.interface.current_window.on_show()
|
|
30
32
|
|
|
31
33
|
@objc_method
|
|
32
34
|
def application_didFinishLaunchingWithOptions_(
|
|
@@ -35,6 +37,7 @@ class PythonAppDelegate(UIResponder):
|
|
|
35
37
|
print("App finished launching.")
|
|
36
38
|
App.app.native = application
|
|
37
39
|
App.app.create()
|
|
40
|
+
App.app.interface.current_window.on_show()
|
|
38
41
|
return True
|
|
39
42
|
|
|
40
43
|
@objc_method
|
|
@@ -51,6 +54,12 @@ class PythonAppDelegate(UIResponder):
|
|
|
51
54
|
|
|
52
55
|
|
|
53
56
|
class App:
|
|
57
|
+
# iOS apps exit when the last window is closed
|
|
58
|
+
CLOSE_ON_LAST_WINDOW = True
|
|
59
|
+
# iOS doesn't have command line handling;
|
|
60
|
+
# but saying it does shortcuts the default handling
|
|
61
|
+
HANDLES_COMMAND_LINE = True
|
|
62
|
+
|
|
54
63
|
def __init__(self, interface):
|
|
55
64
|
self.interface = interface
|
|
56
65
|
self.interface._impl = self
|
|
@@ -67,14 +76,25 @@ class App:
|
|
|
67
76
|
"""Calls the startup method on the interface."""
|
|
68
77
|
self.interface._startup()
|
|
69
78
|
|
|
70
|
-
|
|
71
|
-
|
|
79
|
+
######################################################################
|
|
80
|
+
# Commands and menus
|
|
81
|
+
######################################################################
|
|
82
|
+
|
|
83
|
+
def create_standard_commands(self):
|
|
72
84
|
pass
|
|
73
85
|
|
|
74
86
|
def create_menus(self):
|
|
75
87
|
# No menus on an iOS app (for now)
|
|
76
88
|
pass
|
|
77
89
|
|
|
90
|
+
######################################################################
|
|
91
|
+
# App lifecycle
|
|
92
|
+
######################################################################
|
|
93
|
+
|
|
94
|
+
def exit(self): # pragma: no cover
|
|
95
|
+
# Mobile apps can't be exited, but the entry point needs to exist
|
|
96
|
+
pass
|
|
97
|
+
|
|
78
98
|
def main_loop(self):
|
|
79
99
|
# Main loop is non-blocking on iOS. The app loop is integrated with the
|
|
80
100
|
# main iOS event loop, so this call will return; however, it will leave
|
|
@@ -82,36 +102,48 @@ class App:
|
|
|
82
102
|
# iOS event loop.
|
|
83
103
|
self.loop.run_forever_cooperatively(lifecycle=iOSLifecycle())
|
|
84
104
|
|
|
105
|
+
def set_icon(self, icon):
|
|
106
|
+
# iOS apps don't have runtime icons, so this can't be invoked
|
|
107
|
+
pass # pragma: no cover
|
|
108
|
+
|
|
85
109
|
def set_main_window(self, window):
|
|
86
|
-
|
|
110
|
+
if window is None or window == toga.App.BACKGROUND:
|
|
111
|
+
raise ValueError("Apps without main windows are not supported on iOS")
|
|
87
112
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
113
|
+
######################################################################
|
|
114
|
+
# App resources
|
|
115
|
+
######################################################################
|
|
91
116
|
|
|
92
|
-
def
|
|
93
|
-
|
|
94
|
-
pass
|
|
117
|
+
def get_screens(self):
|
|
118
|
+
return [ScreenImpl(UIScreen.mainScreen)]
|
|
95
119
|
|
|
96
|
-
|
|
97
|
-
|
|
120
|
+
######################################################################
|
|
121
|
+
# App state
|
|
122
|
+
######################################################################
|
|
123
|
+
|
|
124
|
+
def get_dark_mode_state(self):
|
|
125
|
+
self.interface.factory.not_implemented("dark mode state")
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
######################################################################
|
|
129
|
+
# App capabilities
|
|
130
|
+
######################################################################
|
|
98
131
|
|
|
99
132
|
def beep(self):
|
|
100
133
|
# 1013 is a magic constant that is the "SMS RECEIVED 5" sound,
|
|
101
134
|
# sounding like a single strike of a bell.
|
|
102
135
|
av_foundation.AudioServicesPlayAlertSound(1013)
|
|
103
136
|
|
|
104
|
-
def
|
|
105
|
-
|
|
137
|
+
def open_document(self, fileURL): # pragma: no cover
|
|
138
|
+
"""Add a new document to this app."""
|
|
106
139
|
pass
|
|
107
140
|
|
|
108
|
-
def
|
|
109
|
-
|
|
110
|
-
pass
|
|
141
|
+
def show_about_dialog(self):
|
|
142
|
+
self.interface.factory.not_implemented("App.show_about_dialog()")
|
|
111
143
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
144
|
+
######################################################################
|
|
145
|
+
# Cursor control
|
|
146
|
+
######################################################################
|
|
115
147
|
|
|
116
148
|
def hide_cursor(self):
|
|
117
149
|
# No-op; mobile doesn't support cursors
|
|
@@ -120,3 +152,15 @@ class App:
|
|
|
120
152
|
def show_cursor(self):
|
|
121
153
|
# No-op; mobile doesn't support cursors
|
|
122
154
|
pass
|
|
155
|
+
|
|
156
|
+
######################################################################
|
|
157
|
+
# Window control
|
|
158
|
+
######################################################################
|
|
159
|
+
|
|
160
|
+
def get_current_window(self):
|
|
161
|
+
# iOS only has a main window.
|
|
162
|
+
return self.interface.main_window._impl
|
|
163
|
+
|
|
164
|
+
def set_current_window(self, window):
|
|
165
|
+
# iOS only has a main window, so this is a no-op
|
|
166
|
+
pass
|
|
@@ -11,10 +11,9 @@ def native_color(c):
|
|
|
11
11
|
try:
|
|
12
12
|
color = CACHE[c]
|
|
13
13
|
except KeyError:
|
|
14
|
-
# Color needs to be retained to be kept in the cache
|
|
15
14
|
color = UIColor.colorWithRed(
|
|
16
15
|
c.rgba.r / 255, green=c.rgba.g / 255, blue=c.rgba.b / 255, alpha=c.rgba.a
|
|
17
|
-
)
|
|
16
|
+
)
|
|
18
17
|
CACHE[c] = color
|
|
19
18
|
|
|
20
19
|
return color
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from toga import Command as StandardCommand
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Command:
|
|
5
|
+
def __init__(self, interface):
|
|
6
|
+
self.interface = interface
|
|
7
|
+
self.native = []
|
|
8
|
+
|
|
9
|
+
@classmethod
|
|
10
|
+
def standard(cls, app, id):
|
|
11
|
+
if id in {
|
|
12
|
+
StandardCommand.ABOUT,
|
|
13
|
+
StandardCommand.EXIT,
|
|
14
|
+
StandardCommand.NEW,
|
|
15
|
+
StandardCommand.OPEN,
|
|
16
|
+
StandardCommand.PREFERENCES,
|
|
17
|
+
StandardCommand.SAVE,
|
|
18
|
+
StandardCommand.SAVE_AS,
|
|
19
|
+
StandardCommand.SAVE_ALL,
|
|
20
|
+
StandardCommand.VISIT_HOMEPAGE,
|
|
21
|
+
}:
|
|
22
|
+
# These are valid commands, but they're not defined on iOS.
|
|
23
|
+
return None
|
|
24
|
+
|
|
25
|
+
raise ValueError(f"Unknown standard command {id!r}")
|
|
26
|
+
|
|
27
|
+
def set_enabled(self, value):
|
|
28
|
+
pass
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
from toga_iOS.libs import (
|
|
2
|
+
NSLayoutAttributeBottom,
|
|
3
|
+
NSLayoutAttributeLeft,
|
|
4
|
+
NSLayoutAttributeRight,
|
|
5
|
+
NSLayoutAttributeTop,
|
|
6
|
+
NSLayoutConstraint,
|
|
7
|
+
NSLayoutRelationEqual,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Constraints:
|
|
12
|
+
def __init__(self, widget):
|
|
13
|
+
"""A wrapper object storing the constraints required to position a widget at a
|
|
14
|
+
precise location in its container.
|
|
15
|
+
|
|
16
|
+
:param widget: The Widget implementation to be constrained.
|
|
17
|
+
"""
|
|
18
|
+
self.widget = widget
|
|
19
|
+
self.widget.native.translatesAutoresizingMaskIntoConstraints = False
|
|
20
|
+
|
|
21
|
+
self._container = None
|
|
22
|
+
|
|
23
|
+
self.width_constraint = None
|
|
24
|
+
self.height_constraint = None
|
|
25
|
+
|
|
26
|
+
self.left_constraint = None
|
|
27
|
+
self.top_constraint = None
|
|
28
|
+
|
|
29
|
+
# Deletion isn't an event we can programmatically invoke; deletion
|
|
30
|
+
# of constraints can take several iterations before it occurs.
|
|
31
|
+
def __del__(self): # pragma: nocover
|
|
32
|
+
self._remove_constraints()
|
|
33
|
+
|
|
34
|
+
def _remove_constraints(self):
|
|
35
|
+
if self.container:
|
|
36
|
+
# print(f"Remove constraints for {self.widget} in {self.container}")
|
|
37
|
+
# Due to the unpredictability of garbage collection, it's possible for
|
|
38
|
+
# the native object of the window's container to be deleted on the ObjC
|
|
39
|
+
# side before the constraints for the window have been removed. Protect
|
|
40
|
+
# against this possibility.
|
|
41
|
+
if self.container.native:
|
|
42
|
+
self.container.native.removeConstraint(self.width_constraint)
|
|
43
|
+
self.container.native.removeConstraint(self.height_constraint)
|
|
44
|
+
self.container.native.removeConstraint(self.left_constraint)
|
|
45
|
+
self.container.native.removeConstraint(self.top_constraint)
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def container(self):
|
|
49
|
+
return self._container
|
|
50
|
+
|
|
51
|
+
@container.setter
|
|
52
|
+
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
|
+
|
|
56
|
+
self._remove_constraints()
|
|
57
|
+
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
|
+
# )
|
|
63
|
+
self.left_constraint = NSLayoutConstraint.constraintWithItem(
|
|
64
|
+
self.widget.native,
|
|
65
|
+
attribute__1=NSLayoutAttributeLeft,
|
|
66
|
+
relatedBy=NSLayoutRelationEqual,
|
|
67
|
+
toItem=self.container.native,
|
|
68
|
+
attribute__2=NSLayoutAttributeLeft,
|
|
69
|
+
multiplier=1.0,
|
|
70
|
+
constant=10, # Use a dummy, non-zero value for now
|
|
71
|
+
)
|
|
72
|
+
self.container.native.addConstraint(self.left_constraint)
|
|
73
|
+
|
|
74
|
+
self.top_constraint = NSLayoutConstraint.constraintWithItem(
|
|
75
|
+
self.widget.native,
|
|
76
|
+
attribute__1=NSLayoutAttributeTop,
|
|
77
|
+
relatedBy=NSLayoutRelationEqual,
|
|
78
|
+
toItem=self.container.native,
|
|
79
|
+
attribute__2=NSLayoutAttributeTop,
|
|
80
|
+
multiplier=1.0,
|
|
81
|
+
constant=5, # Use a dummy, non-zero value for now
|
|
82
|
+
)
|
|
83
|
+
self.container.native.addConstraint(self.top_constraint)
|
|
84
|
+
|
|
85
|
+
self.width_constraint = NSLayoutConstraint.constraintWithItem(
|
|
86
|
+
self.widget.native,
|
|
87
|
+
attribute__1=NSLayoutAttributeRight,
|
|
88
|
+
relatedBy=NSLayoutRelationEqual,
|
|
89
|
+
toItem=self.widget.native,
|
|
90
|
+
attribute__2=NSLayoutAttributeLeft,
|
|
91
|
+
multiplier=1.0,
|
|
92
|
+
constant=50, # Use a dummy, non-zero value for now
|
|
93
|
+
)
|
|
94
|
+
self.container.native.addConstraint(self.width_constraint)
|
|
95
|
+
|
|
96
|
+
self.height_constraint = NSLayoutConstraint.constraintWithItem(
|
|
97
|
+
self.widget.native,
|
|
98
|
+
attribute__1=NSLayoutAttributeBottom,
|
|
99
|
+
relatedBy=NSLayoutRelationEqual,
|
|
100
|
+
toItem=self.widget.native,
|
|
101
|
+
attribute__2=NSLayoutAttributeTop,
|
|
102
|
+
multiplier=1.0,
|
|
103
|
+
constant=30, # Use a dummy, non-zero value for now
|
|
104
|
+
)
|
|
105
|
+
self.container.native.addConstraint(self.height_constraint)
|
|
106
|
+
|
|
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
|
|
@@ -72,13 +72,18 @@ class Container(BaseContainer):
|
|
|
72
72
|
|
|
73
73
|
self.layout_native = self.native if layout_native is None else layout_native
|
|
74
74
|
|
|
75
|
+
def __del__(self):
|
|
76
|
+
# Mark the contained native object as explicitly None so that the
|
|
77
|
+
# constraints know the object has been deleted.
|
|
78
|
+
self.native = None
|
|
79
|
+
|
|
75
80
|
@property
|
|
76
81
|
def width(self):
|
|
77
82
|
return self.layout_native.bounds.size.width
|
|
78
83
|
|
|
79
84
|
@property
|
|
80
85
|
def height(self):
|
|
81
|
-
return self.layout_native.bounds.size.height
|
|
86
|
+
return self.layout_native.bounds.size.height - self.top_offset
|
|
82
87
|
|
|
83
88
|
@property
|
|
84
89
|
def top_offset(self):
|
|
@@ -124,7 +129,54 @@ class RootContainer(Container):
|
|
|
124
129
|
layout_native=None,
|
|
125
130
|
on_refresh=None,
|
|
126
131
|
):
|
|
132
|
+
"""A bare content container.
|
|
133
|
+
|
|
134
|
+
This is a container that *doesn't* include a navigation/title bar at the top.
|
|
135
|
+
|
|
136
|
+
:param content: The widget impl that is the container's initial content.
|
|
137
|
+
:param layout_native: The native widget that should be used to provide
|
|
138
|
+
size hints to the layout. This will usually be the container widget
|
|
139
|
+
itself; however, for widgets like ScrollContainer where the layout
|
|
140
|
+
needs to be computed based on a different size to what will be
|
|
141
|
+
rendered, the source of the size can be different.
|
|
142
|
+
:param on_refresh: The callback to be notified when this container's layout is
|
|
143
|
+
refreshed.
|
|
127
144
|
"""
|
|
145
|
+
super().__init__(
|
|
146
|
+
content=content,
|
|
147
|
+
layout_native=layout_native,
|
|
148
|
+
on_refresh=on_refresh,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Construct a UIViewController to hold the root content
|
|
152
|
+
self.controller = UIViewController.alloc().init()
|
|
153
|
+
|
|
154
|
+
# Set the controller's view to be the root content widget
|
|
155
|
+
self.controller.view = self.native
|
|
156
|
+
|
|
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
|
+
@property
|
|
163
|
+
def title(self): # pragma: no cover
|
|
164
|
+
return self._title
|
|
165
|
+
|
|
166
|
+
@title.setter
|
|
167
|
+
def title(self, value):
|
|
168
|
+
self._title = value
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class NavigationContainer(Container):
|
|
172
|
+
def __init__(
|
|
173
|
+
self,
|
|
174
|
+
content=None,
|
|
175
|
+
layout_native=None,
|
|
176
|
+
on_refresh=None,
|
|
177
|
+
):
|
|
178
|
+
"""A top level container that provides a navigation/title bar.
|
|
179
|
+
|
|
128
180
|
:param content: The widget impl that is the container's initial content.
|
|
129
181
|
:param layout_native: The native widget that should be used to provide
|
|
130
182
|
size hints to the layout. This will usually be the container widget
|
|
@@ -151,10 +203,6 @@ class RootContainer(Container):
|
|
|
151
203
|
# Set the controller's view to be the root content widget
|
|
152
204
|
self.content_controller.view = self.native
|
|
153
205
|
|
|
154
|
-
@property
|
|
155
|
-
def height(self):
|
|
156
|
-
return self.layout_native.bounds.size.height - self.top_offset
|
|
157
|
-
|
|
158
206
|
@property
|
|
159
207
|
def top_offset(self):
|
|
160
208
|
return (
|