pythonnative 0.14.0__tar.gz → 0.15.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.
- {pythonnative-0.14.0/src/pythonnative.egg-info → pythonnative-0.15.0}/PKG-INFO +5 -4
- {pythonnative-0.14.0 → pythonnative-0.15.0}/README.md +4 -3
- {pythonnative-0.14.0 → pythonnative-0.15.0}/pyproject.toml +2 -1
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/__init__.py +77 -5
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/animated.py +2 -2
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/components.py +41 -33
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/native_views/__init__.py +62 -14
- pythonnative-0.15.0/src/pythonnative/sdk/__init__.py +132 -0
- pythonnative-0.15.0/src/pythonnative/sdk/_components.py +429 -0
- pythonnative-0.15.0/src/pythonnative/style.py +548 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0/src/pythonnative.egg-info}/PKG-INFO +5 -4
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative.egg-info/SOURCES.txt +3 -0
- pythonnative-0.15.0/tests/test_sdk.py +446 -0
- pythonnative-0.15.0/tests/test_style.py +170 -0
- pythonnative-0.14.0/src/pythonnative/style.py +0 -196
- pythonnative-0.14.0/tests/test_style.py +0 -80
- {pythonnative-0.14.0 → pythonnative-0.15.0}/LICENSE +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/setup.cfg +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/_ios_log.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/alerts.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/cli/__init__.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/cli/pn.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/element.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/hooks.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/hot_reload.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/layout.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/native_modules/__init__.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/native_modules/camera.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/native_modules/file_system.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/native_modules/location.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/native_modules/notifications.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/native_views/android.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/native_views/base.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/native_views/ios.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/navigation.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/platform.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/platform_metrics.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/reconciler.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/screen.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/build.gradle +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/proguard-rules.pro +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/androidTest/java/com/pythonnative/android_template/ExampleInstrumentedTest.kt +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/AndroidManifest.xml +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/Navigator.kt +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNVirtualListView.java +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/res/drawable/ic_launcher_background.xml +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/res/layout/activity_main.xml +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/res/navigation/nav_graph.xml +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/res/values/colors.xml +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/res/values/strings.xml +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/res/values/themes.xml +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/res/values-night/themes.xml +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/res/xml/backup_rules.xml +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/main/res/xml/data_extraction_rules.xml +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/app/src/test/java/com/pythonnative/android_template/ExampleUnitTest.kt +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/build.gradle +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.jar +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.properties +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/gradle.properties +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/gradlew +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/gradlew.bat +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/android_template/settings.gradle +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/ios_template/ios_template/AppDelegate.swift +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/AccentColor.colorset/Contents.json +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/Contents.json +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/ios_template/ios_template/Base.lproj/LaunchScreen.storyboard +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/ios_template/ios_template/Base.lproj/Main.storyboard +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/ios_template/ios_template/Info.plist +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/ios_template/ios_template/ViewController.swift +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/ios_template/ios_template.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/ios_template/ios_templateTests/ios_templateTests.swift +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITests.swift +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITestsLaunchTests.swift +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative/utils.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative.egg-info/dependency_links.txt +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative.egg-info/entry_points.txt +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative.egg-info/requires.txt +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/src/pythonnative.egg-info/top_level.txt +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/tests/test_alert.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/tests/test_animated.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/tests/test_cli.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/tests/test_components.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/tests/test_element.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/tests/test_hooks.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/tests/test_hot_reload.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/tests/test_ios_log.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/tests/test_layout.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/tests/test_metric_hooks.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/tests/test_native_views.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/tests/test_navigation.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/tests/test_new_components.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/tests/test_platform.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/tests/test_platform_metrics.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/tests/test_reconciler.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/tests/test_ref.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/tests/test_screen.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/tests/test_smoke.py +0 -0
- {pythonnative-0.14.0 → pythonnative-0.15.0}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pythonnative
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.15.0
|
|
4
4
|
Summary: Cross-platform native UI toolkit for Android and iOS
|
|
5
5
|
Author: Owen Carey
|
|
6
6
|
License: MIT License
|
|
@@ -96,10 +96,11 @@ PythonNative is a cross-platform toolkit for building native Android and iOS app
|
|
|
96
96
|
|
|
97
97
|
- **Declarative UI:** Describe *what* your UI should look like with element functions (`Text`, `Button`, `Column`, `Row`, etc.). PythonNative creates and updates native views automatically.
|
|
98
98
|
- **Hooks and function components:** Manage state with `use_state`, side effects with `use_effect`, and navigation with `use_navigation`, all through one consistent pattern.
|
|
99
|
-
-
|
|
99
|
+
- **Typed `style` prop:** Pass all visual and layout properties through a single `style` dict, fully described by the `pn.Style` `TypedDict` and the ergonomic `pn.style(...)` helper for IDE autocomplete and static checking. Compose reusable styles with `StyleSheet`.
|
|
100
100
|
- **Cross-platform flexbox engine:** A pure-Python, Yoga-style layout engine computes frames once and applies them to native views, so `flex`, `padding`, `aspect_ratio`, and `position: "absolute"` produce the same geometry on Android and iOS.
|
|
101
101
|
- **Virtual view tree + reconciler:** Element trees are diffed and patched with minimal native mutations, similar to React's reconciliation.
|
|
102
102
|
- **Direct native bindings:** Python calls platform APIs directly through Chaquopy and rubicon-objc, with no JavaScript bridge.
|
|
103
|
+
- **Custom-component SDK:** Wrap any platform widget as a first-class element with type-checked props via `pythonnative.sdk` (`Props`, `@native_component`, `element_factory`). Plugins distributed on PyPI auto-register through the `pythonnative.handlers` entry-point group.
|
|
103
104
|
- **CLI scaffolding:** `pn init` creates a ready-to-run project; `pn run android` and `pn run ios` build and launch your app.
|
|
104
105
|
- **Native-backed navigation:** Declarative `Stack`, `Tab`, and `Drawer` navigators inspired by React Navigation. The root stack drives the platform's native navigation controller (`UINavigationController` on iOS, AndroidX Navigation Component on Android), so transitions, back gestures, and the hardware back button match what users expect.
|
|
105
106
|
- **Fast Refresh hot reload:** `pn run --hot-reload` watches `app/` and patches edits into the running app on save, preserving component state across most changes.
|
|
@@ -123,12 +124,12 @@ import pythonnative as pn
|
|
|
123
124
|
def App():
|
|
124
125
|
count, set_count = pn.use_state(0)
|
|
125
126
|
return pn.Column(
|
|
126
|
-
pn.Text(f"Count: {count}", style=
|
|
127
|
+
pn.Text(f"Count: {count}", style=pn.style(font_size=24, bold=True)),
|
|
127
128
|
pn.Button(
|
|
128
129
|
"Tap me",
|
|
129
130
|
on_click=lambda: set_count(count + 1),
|
|
130
131
|
),
|
|
131
|
-
style=
|
|
132
|
+
style=pn.style(spacing=12, padding=16),
|
|
132
133
|
)
|
|
133
134
|
```
|
|
134
135
|
|
|
@@ -32,10 +32,11 @@ PythonNative is a cross-platform toolkit for building native Android and iOS app
|
|
|
32
32
|
|
|
33
33
|
- **Declarative UI:** Describe *what* your UI should look like with element functions (`Text`, `Button`, `Column`, `Row`, etc.). PythonNative creates and updates native views automatically.
|
|
34
34
|
- **Hooks and function components:** Manage state with `use_state`, side effects with `use_effect`, and navigation with `use_navigation`, all through one consistent pattern.
|
|
35
|
-
-
|
|
35
|
+
- **Typed `style` prop:** Pass all visual and layout properties through a single `style` dict, fully described by the `pn.Style` `TypedDict` and the ergonomic `pn.style(...)` helper for IDE autocomplete and static checking. Compose reusable styles with `StyleSheet`.
|
|
36
36
|
- **Cross-platform flexbox engine:** A pure-Python, Yoga-style layout engine computes frames once and applies them to native views, so `flex`, `padding`, `aspect_ratio`, and `position: "absolute"` produce the same geometry on Android and iOS.
|
|
37
37
|
- **Virtual view tree + reconciler:** Element trees are diffed and patched with minimal native mutations, similar to React's reconciliation.
|
|
38
38
|
- **Direct native bindings:** Python calls platform APIs directly through Chaquopy and rubicon-objc, with no JavaScript bridge.
|
|
39
|
+
- **Custom-component SDK:** Wrap any platform widget as a first-class element with type-checked props via `pythonnative.sdk` (`Props`, `@native_component`, `element_factory`). Plugins distributed on PyPI auto-register through the `pythonnative.handlers` entry-point group.
|
|
39
40
|
- **CLI scaffolding:** `pn init` creates a ready-to-run project; `pn run android` and `pn run ios` build and launch your app.
|
|
40
41
|
- **Native-backed navigation:** Declarative `Stack`, `Tab`, and `Drawer` navigators inspired by React Navigation. The root stack drives the platform's native navigation controller (`UINavigationController` on iOS, AndroidX Navigation Component on Android), so transitions, back gestures, and the hardware back button match what users expect.
|
|
41
42
|
- **Fast Refresh hot reload:** `pn run --hot-reload` watches `app/` and patches edits into the running app on save, preserving component state across most changes.
|
|
@@ -59,12 +60,12 @@ import pythonnative as pn
|
|
|
59
60
|
def App():
|
|
60
61
|
count, set_count = pn.use_state(0)
|
|
61
62
|
return pn.Column(
|
|
62
|
-
pn.Text(f"Count: {count}", style=
|
|
63
|
+
pn.Text(f"Count: {count}", style=pn.style(font_size=24, bold=True)),
|
|
63
64
|
pn.Button(
|
|
64
65
|
"Tap me",
|
|
65
66
|
on_click=lambda: set_count(count + 1),
|
|
66
67
|
),
|
|
67
|
-
style=
|
|
68
|
+
style=pn.style(spacing=12, padding=16),
|
|
68
69
|
)
|
|
69
70
|
```
|
|
70
71
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pythonnative"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.15.0"
|
|
8
8
|
description = "Cross-platform native UI toolkit for Android and iOS"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "Owen Carey" }
|
|
@@ -64,6 +64,7 @@ Documentation = "https://docs.pythonnative.com/"
|
|
|
64
64
|
|
|
65
65
|
|
|
66
66
|
|
|
67
|
+
|
|
67
68
|
[tool.setuptools.packages.find]
|
|
68
69
|
where = ["src"]
|
|
69
70
|
|
|
@@ -23,8 +23,18 @@ Key building blocks:
|
|
|
23
23
|
factories.
|
|
24
24
|
- **Styling** uses a single ``style`` dict per element (or a list of
|
|
25
25
|
dicts), composable via [`StyleSheet`][pythonnative.StyleSheet].
|
|
26
|
+
PythonNative ships a fully-typed [`Style`][pythonnative.style.Style]
|
|
27
|
+
TypedDict so editors and ``mypy`` validate every key as you type.
|
|
26
28
|
- **Animations** use the ``Animated`` namespace, modeled on React
|
|
27
29
|
Native's animation API.
|
|
30
|
+
- **Custom native components** can be authored with the
|
|
31
|
+
``pythonnative.sdk`` package: define a typed
|
|
32
|
+
[`Props`][pythonnative.sdk.Props] dataclass, implement a
|
|
33
|
+
[`ViewHandler`][pythonnative.native_views.base.ViewHandler] for each
|
|
34
|
+
platform, and register it via
|
|
35
|
+
[`@native_component`][pythonnative.sdk.native_component] (or expose
|
|
36
|
+
it from a PyPI package via the ``pythonnative.handlers`` entry-point
|
|
37
|
+
group).
|
|
28
38
|
|
|
29
39
|
Example:
|
|
30
40
|
```python
|
|
@@ -34,15 +44,16 @@ Example:
|
|
|
34
44
|
def App():
|
|
35
45
|
count, set_count = pn.use_state(0)
|
|
36
46
|
return pn.Column(
|
|
37
|
-
pn.Text(f"Count: {count}", style=
|
|
47
|
+
pn.Text(f"Count: {count}", style=pn.style(font_size=24)),
|
|
38
48
|
pn.Button("+", on_click=lambda: set_count(count + 1)),
|
|
39
|
-
style=
|
|
49
|
+
style=pn.style(spacing=12),
|
|
40
50
|
)
|
|
41
51
|
```
|
|
42
52
|
"""
|
|
43
53
|
|
|
44
|
-
__version__ = "0.
|
|
54
|
+
__version__ = "0.15.0"
|
|
45
55
|
|
|
56
|
+
from . import sdk
|
|
46
57
|
from .alerts import Alert
|
|
47
58
|
from .animated import Animated, AnimatedValue
|
|
48
59
|
from .components import (
|
|
@@ -100,7 +111,39 @@ from .navigation import (
|
|
|
100
111
|
)
|
|
101
112
|
from .platform import Platform
|
|
102
113
|
from .screen import create_screen
|
|
103
|
-
from .
|
|
114
|
+
from .sdk import (
|
|
115
|
+
Props,
|
|
116
|
+
ViewHandler,
|
|
117
|
+
element_factory,
|
|
118
|
+
native_component,
|
|
119
|
+
register_component,
|
|
120
|
+
)
|
|
121
|
+
from .style import (
|
|
122
|
+
AlignItems,
|
|
123
|
+
AlignSelf,
|
|
124
|
+
AutoCapitalize,
|
|
125
|
+
Color,
|
|
126
|
+
Dimension,
|
|
127
|
+
EdgeInsets,
|
|
128
|
+
FlexDirection,
|
|
129
|
+
FontWeight,
|
|
130
|
+
JustifyContent,
|
|
131
|
+
KeyboardType,
|
|
132
|
+
Overflow,
|
|
133
|
+
Position,
|
|
134
|
+
ReturnKeyType,
|
|
135
|
+
ScaleType,
|
|
136
|
+
ShadowOffset,
|
|
137
|
+
Style,
|
|
138
|
+
StyleProp,
|
|
139
|
+
StyleSheet,
|
|
140
|
+
TextAlign,
|
|
141
|
+
TextDecoration,
|
|
142
|
+
ThemeContext,
|
|
143
|
+
TransformSpec,
|
|
144
|
+
resolve_style,
|
|
145
|
+
style,
|
|
146
|
+
)
|
|
104
147
|
|
|
105
148
|
__all__ = [
|
|
106
149
|
# Components
|
|
@@ -154,9 +197,31 @@ __all__ = [
|
|
|
154
197
|
"create_drawer_navigator",
|
|
155
198
|
"create_stack_navigator",
|
|
156
199
|
"create_tab_navigator",
|
|
157
|
-
# Styling
|
|
200
|
+
# Styling - typed primitives
|
|
201
|
+
"AlignItems",
|
|
202
|
+
"AlignSelf",
|
|
203
|
+
"AutoCapitalize",
|
|
204
|
+
"Color",
|
|
205
|
+
"Dimension",
|
|
206
|
+
"EdgeInsets",
|
|
207
|
+
"FlexDirection",
|
|
208
|
+
"FontWeight",
|
|
209
|
+
"JustifyContent",
|
|
210
|
+
"KeyboardType",
|
|
211
|
+
"Overflow",
|
|
212
|
+
"Position",
|
|
213
|
+
"ReturnKeyType",
|
|
214
|
+
"ScaleType",
|
|
215
|
+
"ShadowOffset",
|
|
216
|
+
"Style",
|
|
217
|
+
"StyleProp",
|
|
158
218
|
"StyleSheet",
|
|
219
|
+
"TextAlign",
|
|
220
|
+
"TextDecoration",
|
|
159
221
|
"ThemeContext",
|
|
222
|
+
"TransformSpec",
|
|
223
|
+
"resolve_style",
|
|
224
|
+
"style",
|
|
160
225
|
# Animation
|
|
161
226
|
"Animated",
|
|
162
227
|
"AnimatedValue",
|
|
@@ -169,4 +234,11 @@ __all__ = [
|
|
|
169
234
|
"Notifications",
|
|
170
235
|
# Platform
|
|
171
236
|
"Platform",
|
|
237
|
+
# Custom-component SDK
|
|
238
|
+
"Props",
|
|
239
|
+
"ViewHandler",
|
|
240
|
+
"element_factory",
|
|
241
|
+
"native_component",
|
|
242
|
+
"register_component",
|
|
243
|
+
"sdk",
|
|
172
244
|
]
|
|
@@ -58,7 +58,7 @@ from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
|
58
58
|
|
|
59
59
|
from .element import Element
|
|
60
60
|
from .hooks import use_effect, use_ref
|
|
61
|
-
from .style import
|
|
61
|
+
from .style import StyleProp, resolve_style
|
|
62
62
|
|
|
63
63
|
# Maximum frame rate at which the Python ticker drives animations.
|
|
64
64
|
# We aim for 60 Hz but back off when no animation is active.
|
|
@@ -433,7 +433,7 @@ class _AnimationHandle:
|
|
|
433
433
|
# ======================================================================
|
|
434
434
|
|
|
435
435
|
|
|
436
|
-
def _resolve_style_with_values(style:
|
|
436
|
+
def _resolve_style_with_values(style: StyleProp) -> Tuple[Dict[str, Any], Dict[str, AnimatedValue]]:
|
|
437
437
|
"""Return ``(plain_style, animated_bindings)``.
|
|
438
438
|
|
|
439
439
|
AnimatedValue entries in the style are replaced with their
|
|
@@ -35,10 +35,18 @@ Example:
|
|
|
35
35
|
```
|
|
36
36
|
"""
|
|
37
37
|
|
|
38
|
-
from typing import Any, Callable, Dict, List, Optional
|
|
38
|
+
from typing import Any, Callable, Dict, List, Literal, Optional
|
|
39
39
|
|
|
40
40
|
from .element import Element
|
|
41
|
-
from .style import
|
|
41
|
+
from .style import (
|
|
42
|
+
AutoCapitalize,
|
|
43
|
+
Color,
|
|
44
|
+
KeyboardType,
|
|
45
|
+
ReturnKeyType,
|
|
46
|
+
ScaleType,
|
|
47
|
+
StyleProp,
|
|
48
|
+
resolve_style,
|
|
49
|
+
)
|
|
42
50
|
|
|
43
51
|
# ======================================================================
|
|
44
52
|
# Leaf components
|
|
@@ -73,7 +81,7 @@ def _accessibility_props(
|
|
|
73
81
|
def Text(
|
|
74
82
|
text: str = "",
|
|
75
83
|
*,
|
|
76
|
-
style:
|
|
84
|
+
style: StyleProp = None,
|
|
77
85
|
accessibility_label: Optional[str] = None,
|
|
78
86
|
accessibility_hint: Optional[str] = None,
|
|
79
87
|
accessibility_role: Optional[str] = None,
|
|
@@ -118,7 +126,7 @@ def Button(
|
|
|
118
126
|
*,
|
|
119
127
|
on_click: Optional[Callable[[], None]] = None,
|
|
120
128
|
enabled: bool = True,
|
|
121
|
-
style:
|
|
129
|
+
style: StyleProp = None,
|
|
122
130
|
accessibility_label: Optional[str] = None,
|
|
123
131
|
accessibility_hint: Optional[str] = None,
|
|
124
132
|
accessible: Optional[bool] = None,
|
|
@@ -174,14 +182,14 @@ def TextInput(
|
|
|
174
182
|
on_submit: Optional[Callable[[str], None]] = None,
|
|
175
183
|
secure: bool = False,
|
|
176
184
|
multiline: bool = False,
|
|
177
|
-
keyboard_type: Optional[
|
|
178
|
-
auto_capitalize: Optional[
|
|
185
|
+
keyboard_type: Optional[KeyboardType] = None,
|
|
186
|
+
auto_capitalize: Optional[AutoCapitalize] = None,
|
|
179
187
|
auto_correct: Optional[bool] = None,
|
|
180
188
|
auto_focus: bool = False,
|
|
181
|
-
return_key_type: Optional[
|
|
189
|
+
return_key_type: Optional[ReturnKeyType] = None,
|
|
182
190
|
max_length: Optional[int] = None,
|
|
183
|
-
placeholder_color: Optional[
|
|
184
|
-
style:
|
|
191
|
+
placeholder_color: Optional[Color] = None,
|
|
192
|
+
style: StyleProp = None,
|
|
185
193
|
accessibility_label: Optional[str] = None,
|
|
186
194
|
accessibility_hint: Optional[str] = None,
|
|
187
195
|
accessible: Optional[bool] = None,
|
|
@@ -257,9 +265,9 @@ def TextInput(
|
|
|
257
265
|
def Image(
|
|
258
266
|
source: str = "",
|
|
259
267
|
*,
|
|
260
|
-
scale_type: Optional[
|
|
261
|
-
tint_color: Optional[
|
|
262
|
-
style:
|
|
268
|
+
scale_type: Optional[ScaleType] = None,
|
|
269
|
+
tint_color: Optional[Color] = None,
|
|
270
|
+
style: StyleProp = None,
|
|
263
271
|
accessibility_label: Optional[str] = None,
|
|
264
272
|
accessible: Optional[bool] = None,
|
|
265
273
|
ref: Optional[Dict[str, Any]] = None,
|
|
@@ -307,7 +315,7 @@ def Switch(
|
|
|
307
315
|
*,
|
|
308
316
|
value: bool = False,
|
|
309
317
|
on_change: Optional[Callable[[bool], None]] = None,
|
|
310
|
-
style:
|
|
318
|
+
style: StyleProp = None,
|
|
311
319
|
key: Optional[str] = None,
|
|
312
320
|
) -> Element:
|
|
313
321
|
"""Display a toggle switch.
|
|
@@ -331,7 +339,7 @@ def Switch(
|
|
|
331
339
|
def ProgressBar(
|
|
332
340
|
*,
|
|
333
341
|
value: float = 0.0,
|
|
334
|
-
style:
|
|
342
|
+
style: StyleProp = None,
|
|
335
343
|
key: Optional[str] = None,
|
|
336
344
|
) -> Element:
|
|
337
345
|
"""Show determinate progress as a value between 0.0 and 1.0.
|
|
@@ -356,7 +364,7 @@ def ProgressBar(
|
|
|
356
364
|
def ActivityIndicator(
|
|
357
365
|
*,
|
|
358
366
|
animating: bool = True,
|
|
359
|
-
style:
|
|
367
|
+
style: StyleProp = None,
|
|
360
368
|
key: Optional[str] = None,
|
|
361
369
|
) -> Element:
|
|
362
370
|
"""Show an indeterminate loading spinner.
|
|
@@ -378,7 +386,7 @@ def ActivityIndicator(
|
|
|
378
386
|
def WebView(
|
|
379
387
|
*,
|
|
380
388
|
url: str = "",
|
|
381
|
-
style:
|
|
389
|
+
style: StyleProp = None,
|
|
382
390
|
key: Optional[str] = None,
|
|
383
391
|
) -> Element:
|
|
384
392
|
"""Embed web content from a URL.
|
|
@@ -440,7 +448,7 @@ def Slider(
|
|
|
440
448
|
min_value: float = 0.0,
|
|
441
449
|
max_value: float = 1.0,
|
|
442
450
|
on_change: Optional[Callable[[float], None]] = None,
|
|
443
|
-
style:
|
|
451
|
+
style: StyleProp = None,
|
|
444
452
|
key: Optional[str] = None,
|
|
445
453
|
) -> Element:
|
|
446
454
|
"""Continuous-value slider between `min_value` and `max_value`.
|
|
@@ -475,7 +483,7 @@ def Slider(
|
|
|
475
483
|
|
|
476
484
|
def View(
|
|
477
485
|
*children: Element,
|
|
478
|
-
style:
|
|
486
|
+
style: StyleProp = None,
|
|
479
487
|
accessibility_label: Optional[str] = None,
|
|
480
488
|
accessibility_hint: Optional[str] = None,
|
|
481
489
|
accessibility_role: Optional[str] = None,
|
|
@@ -530,7 +538,7 @@ def View(
|
|
|
530
538
|
|
|
531
539
|
def Column(
|
|
532
540
|
*children: Element,
|
|
533
|
-
style:
|
|
541
|
+
style: StyleProp = None,
|
|
534
542
|
ref: Optional[Dict[str, Any]] = None,
|
|
535
543
|
key: Optional[str] = None,
|
|
536
544
|
) -> Element:
|
|
@@ -571,7 +579,7 @@ def Column(
|
|
|
571
579
|
|
|
572
580
|
def Row(
|
|
573
581
|
*children: Element,
|
|
574
|
-
style:
|
|
582
|
+
style: StyleProp = None,
|
|
575
583
|
ref: Optional[Dict[str, Any]] = None,
|
|
576
584
|
key: Optional[str] = None,
|
|
577
585
|
) -> Element:
|
|
@@ -614,7 +622,7 @@ def ScrollView(
|
|
|
614
622
|
child: Optional[Element] = None,
|
|
615
623
|
*,
|
|
616
624
|
refresh_control: Optional[Dict[str, Any]] = None,
|
|
617
|
-
style:
|
|
625
|
+
style: StyleProp = None,
|
|
618
626
|
ref: Optional[Dict[str, Any]] = None,
|
|
619
627
|
key: Optional[str] = None,
|
|
620
628
|
) -> Element:
|
|
@@ -647,7 +655,7 @@ def ScrollView(
|
|
|
647
655
|
|
|
648
656
|
def SafeAreaView(
|
|
649
657
|
*children: Element,
|
|
650
|
-
style:
|
|
658
|
+
style: StyleProp = None,
|
|
651
659
|
key: Optional[str] = None,
|
|
652
660
|
) -> Element:
|
|
653
661
|
"""Container that respects safe-area insets (notch, status bar, home indicator).
|
|
@@ -670,9 +678,9 @@ def Modal(
|
|
|
670
678
|
visible: bool = False,
|
|
671
679
|
on_dismiss: Optional[Callable[[], None]] = None,
|
|
672
680
|
title: Optional[str] = None,
|
|
673
|
-
animation_type:
|
|
681
|
+
animation_type: Literal["slide", "fade", "none"] = "slide",
|
|
674
682
|
transparent: bool = False,
|
|
675
|
-
style:
|
|
683
|
+
style: StyleProp = None,
|
|
676
684
|
key: Optional[str] = None,
|
|
677
685
|
) -> Element:
|
|
678
686
|
"""Overlay modal dialog backed by a real native presentation.
|
|
@@ -720,7 +728,7 @@ def Pressable(
|
|
|
720
728
|
on_press: Optional[Callable[[], None]] = None,
|
|
721
729
|
on_long_press: Optional[Callable[[], None]] = None,
|
|
722
730
|
pressed_opacity: float = 0.6,
|
|
723
|
-
style:
|
|
731
|
+
style: StyleProp = None,
|
|
724
732
|
accessibility_label: Optional[str] = None,
|
|
725
733
|
accessibility_hint: Optional[str] = None,
|
|
726
734
|
accessible: Optional[bool] = None,
|
|
@@ -811,7 +819,7 @@ def FlatList(
|
|
|
811
819
|
separator_height: float = 0,
|
|
812
820
|
refresh_control: Optional[Dict[str, Any]] = None,
|
|
813
821
|
on_item_press: Optional[Callable[[int], None]] = None,
|
|
814
|
-
style:
|
|
822
|
+
style: StyleProp = None,
|
|
815
823
|
key: Optional[str] = None,
|
|
816
824
|
) -> Element:
|
|
817
825
|
"""Virtualized scrollable list that renders items from `data` lazily.
|
|
@@ -941,7 +949,7 @@ def SectionList(
|
|
|
941
949
|
item_height: Optional[float] = None,
|
|
942
950
|
section_header_height: float = 32.0,
|
|
943
951
|
separator_height: float = 0,
|
|
944
|
-
style:
|
|
952
|
+
style: StyleProp = None,
|
|
945
953
|
key: Optional[str] = None,
|
|
946
954
|
) -> Element:
|
|
947
955
|
"""Virtualized list that supports section headers.
|
|
@@ -1040,8 +1048,8 @@ def SectionList(
|
|
|
1040
1048
|
|
|
1041
1049
|
def StatusBar(
|
|
1042
1050
|
*,
|
|
1043
|
-
style: Optional[
|
|
1044
|
-
background_color: Optional[
|
|
1051
|
+
style: Optional[Literal["light", "dark", "default"]] = None,
|
|
1052
|
+
background_color: Optional[Color] = None,
|
|
1045
1053
|
hidden: Optional[bool] = None,
|
|
1046
1054
|
key: Optional[str] = None,
|
|
1047
1055
|
) -> Element:
|
|
@@ -1075,8 +1083,8 @@ def StatusBar(
|
|
|
1075
1083
|
|
|
1076
1084
|
def KeyboardAvoidingView(
|
|
1077
1085
|
*children: Element,
|
|
1078
|
-
behavior:
|
|
1079
|
-
style:
|
|
1086
|
+
behavior: Literal["padding", "position"] = "padding",
|
|
1087
|
+
style: StyleProp = None,
|
|
1080
1088
|
key: Optional[str] = None,
|
|
1081
1089
|
) -> Element:
|
|
1082
1090
|
"""Wrap content that should shift up when the keyboard is shown.
|
|
@@ -1106,7 +1114,7 @@ def RefreshControl(
|
|
|
1106
1114
|
*,
|
|
1107
1115
|
refreshing: bool = False,
|
|
1108
1116
|
on_refresh: Optional[Callable[[], None]] = None,
|
|
1109
|
-
tint_color: Optional[
|
|
1117
|
+
tint_color: Optional[Color] = None,
|
|
1110
1118
|
) -> Dict[str, Any]:
|
|
1111
1119
|
"""Pull-to-refresh spec for [`ScrollView`][pythonnative.ScrollView] / [`FlatList`][pythonnative.FlatList].
|
|
1112
1120
|
|
|
@@ -1162,7 +1170,7 @@ def Picker(
|
|
|
1162
1170
|
items: Optional[List[Dict[str, Any]]] = None,
|
|
1163
1171
|
on_change: Optional[Callable[[Any], None]] = None,
|
|
1164
1172
|
placeholder: str = "Select…",
|
|
1165
|
-
style:
|
|
1173
|
+
style: StyleProp = None,
|
|
1166
1174
|
key: Optional[str] = None,
|
|
1167
1175
|
) -> Element:
|
|
1168
1176
|
"""A select / dropdown widget.
|
|
@@ -237,12 +237,50 @@ class NativeViewRegistry:
|
|
|
237
237
|
_registry: Optional[NativeViewRegistry] = None
|
|
238
238
|
|
|
239
239
|
|
|
240
|
+
def _active_platform_name() -> str:
|
|
241
|
+
"""Return ``"android"`` or ``"ios"`` for the active runtime."""
|
|
242
|
+
from ..utils import IS_ANDROID
|
|
243
|
+
|
|
244
|
+
return "android" if IS_ANDROID else "ios"
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _register_builtin_handlers(registry: NativeViewRegistry) -> None:
|
|
248
|
+
"""Register every built-in handler for the active platform."""
|
|
249
|
+
from ..utils import IS_ANDROID
|
|
250
|
+
|
|
251
|
+
if IS_ANDROID:
|
|
252
|
+
from .android import register_handlers
|
|
253
|
+
else:
|
|
254
|
+
from .ios import register_handlers
|
|
255
|
+
register_handlers(registry)
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def _install_sdk_handlers(registry: NativeViewRegistry) -> None:
|
|
259
|
+
"""Copy decorator-registered SDK handlers + entry-point plugins.
|
|
260
|
+
|
|
261
|
+
Imported lazily so unit tests that never touch the SDK don't pay the
|
|
262
|
+
entry-point discovery cost.
|
|
263
|
+
"""
|
|
264
|
+
try:
|
|
265
|
+
from ..sdk._components import install_into_registry as _sdk_install
|
|
266
|
+
except Exception:
|
|
267
|
+
return
|
|
268
|
+
try:
|
|
269
|
+
_sdk_install(registry, _active_platform_name())
|
|
270
|
+
except Exception:
|
|
271
|
+
# A misbehaving plugin must not break PythonNative's startup.
|
|
272
|
+
pass
|
|
273
|
+
|
|
274
|
+
|
|
240
275
|
def get_registry() -> NativeViewRegistry:
|
|
241
276
|
"""Return the process-wide registry, lazily registering handlers.
|
|
242
277
|
|
|
243
|
-
The first call instantiates the registry
|
|
244
|
-
Android or iOS handlers based on `IS_ANDROID
|
|
245
|
-
|
|
278
|
+
The first call instantiates the registry, registers either the
|
|
279
|
+
Android or iOS handlers based on `IS_ANDROID`, then layers on every
|
|
280
|
+
decorator-registered SDK handler (and any handlers exposed by
|
|
281
|
+
third-party packages via the
|
|
282
|
+
[`pythonnative.handlers`][pythonnative.sdk.ENTRY_POINT_GROUP] entry
|
|
283
|
+
point group). Subsequent calls return the same instance.
|
|
246
284
|
|
|
247
285
|
Returns:
|
|
248
286
|
The active `NativeViewRegistry`.
|
|
@@ -251,30 +289,40 @@ def get_registry() -> NativeViewRegistry:
|
|
|
251
289
|
if _registry is not None:
|
|
252
290
|
return _registry
|
|
253
291
|
_registry = NativeViewRegistry()
|
|
292
|
+
_register_builtin_handlers(_registry)
|
|
293
|
+
_install_sdk_handlers(_registry)
|
|
294
|
+
return _registry
|
|
254
295
|
|
|
255
|
-
from ..utils import IS_ANDROID
|
|
256
296
|
|
|
257
|
-
|
|
258
|
-
|
|
297
|
+
def refresh_registry() -> NativeViewRegistry:
|
|
298
|
+
"""Re-run SDK handler installation against the existing registry.
|
|
259
299
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
300
|
+
Call this after registering a new component at runtime if the
|
|
301
|
+
registry has already been instantiated. This is mostly useful in
|
|
302
|
+
REPL sessions and tests; the normal flow is "register, then call
|
|
303
|
+
[`get_registry`][pythonnative.native_views.get_registry]" and the
|
|
304
|
+
handlers come along automatically.
|
|
263
305
|
|
|
264
|
-
|
|
265
|
-
|
|
306
|
+
Returns:
|
|
307
|
+
The active `NativeViewRegistry`.
|
|
308
|
+
"""
|
|
309
|
+
registry = get_registry()
|
|
310
|
+
_install_sdk_handlers(registry)
|
|
311
|
+
return registry
|
|
266
312
|
|
|
267
313
|
|
|
268
|
-
def set_registry(registry: NativeViewRegistry) -> None:
|
|
314
|
+
def set_registry(registry: Optional[NativeViewRegistry]) -> None:
|
|
269
315
|
"""Install a custom registry (primarily for testing).
|
|
270
316
|
|
|
271
317
|
Replaces the lazy singleton so subsequent
|
|
272
318
|
[`get_registry`][pythonnative.native_views.get_registry] calls
|
|
273
319
|
return `registry`. Pass a mock to drive the reconciler from
|
|
274
|
-
unit tests without touching real native APIs.
|
|
320
|
+
unit tests without touching real native APIs. Pass ``None`` to
|
|
321
|
+
reset the singleton; the next ``get_registry`` call will then
|
|
322
|
+
rebuild it from scratch.
|
|
275
323
|
|
|
276
324
|
Args:
|
|
277
|
-
registry: The replacement registry.
|
|
325
|
+
registry: The replacement registry, or ``None`` to clear.
|
|
278
326
|
"""
|
|
279
327
|
global _registry
|
|
280
328
|
_registry = registry
|