pythonnative 0.6.0__tar.gz → 0.7.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.6.0/src/pythonnative.egg-info → pythonnative-0.7.0}/PKG-INFO +17 -20
- {pythonnative-0.6.0 → pythonnative-0.7.0}/README.md +16 -19
- {pythonnative-0.6.0 → pythonnative-0.7.0}/pyproject.toml +1 -1
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/__init__.py +9 -19
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/cli/pn.py +10 -18
- pythonnative-0.7.0/src/pythonnative/components.py +351 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/hooks.py +49 -2
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/native_views.py +86 -16
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/page.py +138 -172
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/style.py +27 -7
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PageFragment.kt +2 -9
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/ios_template/ios_template/ViewController.swift +7 -20
- {pythonnative-0.6.0 → pythonnative-0.7.0/src/pythonnative.egg-info}/PKG-INFO +17 -20
- {pythonnative-0.6.0 → pythonnative-0.7.0}/tests/test_cli.py +1 -1
- {pythonnative-0.6.0 → pythonnative-0.7.0}/tests/test_components.py +36 -16
- {pythonnative-0.6.0 → pythonnative-0.7.0}/tests/test_hooks.py +32 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/tests/test_smoke.py +3 -1
- {pythonnative-0.6.0 → pythonnative-0.7.0}/tests/test_style.py +23 -1
- pythonnative-0.6.0/src/pythonnative/components.py +0 -563
- {pythonnative-0.6.0 → pythonnative-0.7.0}/LICENSE +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/setup.cfg +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/cli/__init__.py +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/element.py +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/hot_reload.py +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/native_modules/__init__.py +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/native_modules/camera.py +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/native_modules/file_system.py +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/native_modules/location.py +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/native_modules/notifications.py +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/reconciler.py +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/build.gradle +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/proguard-rules.pro +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/androidTest/java/com/pythonnative/android_template/ExampleInstrumentedTest.kt +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/AndroidManifest.xml +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/Navigator.kt +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/res/drawable/ic_launcher_background.xml +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/res/layout/activity_main.xml +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/res/navigation/nav_graph.xml +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/res/values/colors.xml +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/res/values/strings.xml +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/res/values/themes.xml +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/res/values-night/themes.xml +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/res/xml/backup_rules.xml +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/main/res/xml/data_extraction_rules.xml +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/app/src/test/java/com/pythonnative/android_template/ExampleUnitTest.kt +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/build.gradle +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.jar +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.properties +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/gradle.properties +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/gradlew +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/gradlew.bat +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/android_template/settings.gradle +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/ios_template/ios_template/AppDelegate.swift +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/AccentColor.colorset/Contents.json +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/Contents.json +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/ios_template/ios_template/Base.lproj/LaunchScreen.storyboard +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/ios_template/ios_template/Base.lproj/Main.storyboard +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/ios_template/ios_template/Info.plist +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/ios_template/ios_template.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/ios_template/ios_templateTests/ios_templateTests.swift +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITests.swift +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITestsLaunchTests.swift +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative/utils.py +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative.egg-info/SOURCES.txt +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative.egg-info/dependency_links.txt +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative.egg-info/entry_points.txt +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative.egg-info/requires.txt +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/src/pythonnative.egg-info/top_level.txt +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/tests/test_element.py +0 -0
- {pythonnative-0.6.0 → pythonnative-0.7.0}/tests/test_reconciler.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pythonnative
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Cross-platform native UI toolkit for Android and iOS
|
|
5
5
|
Author: Owen Carey
|
|
6
6
|
License: MIT License
|
|
@@ -88,17 +88,18 @@ Dynamic: license-file
|
|
|
88
88
|
|
|
89
89
|
## Overview
|
|
90
90
|
|
|
91
|
-
PythonNative is a cross-platform toolkit for building native Android and iOS apps in Python. It provides a **declarative, React-like component model** with automatic reconciliation, powered by Chaquopy on Android and rubicon-objc on iOS.
|
|
91
|
+
PythonNative is a cross-platform toolkit for building native Android and iOS apps in Python. It provides a **declarative, React-like component model** with hooks and automatic reconciliation, powered by Chaquopy on Android and rubicon-objc on iOS. Write function components with `use_state`, `use_effect`, and friends, just like React, and let PythonNative handle creating and updating native views.
|
|
92
92
|
|
|
93
93
|
## Features
|
|
94
94
|
|
|
95
95
|
- **Declarative UI:** Describe *what* your UI should look like with element functions (`Text`, `Button`, `Column`, `Row`, etc.). PythonNative creates and updates native views automatically.
|
|
96
|
-
- **
|
|
96
|
+
- **Hooks and function components:** Manage state with `use_state`, side effects with `use_effect`, and navigation with `use_navigation`, all through one consistent pattern.
|
|
97
|
+
- **`style` prop:** Pass all visual and layout properties through a single `style` dict, composable via `StyleSheet`.
|
|
97
98
|
- **Virtual view tree + reconciler:** Element trees are diffed and patched with minimal native mutations, similar to React's reconciliation.
|
|
98
99
|
- **Direct native bindings:** Python calls platform APIs directly through Chaquopy and rubicon-objc, with no JavaScript bridge.
|
|
99
100
|
- **CLI scaffolding:** `pn init` creates a ready-to-run project; `pn run android` and `pn run ios` build and launch your app.
|
|
100
|
-
- **Navigation:** Push and pop screens with argument passing
|
|
101
|
-
- **Bundled templates:** Android Gradle and iOS Xcode templates are included
|
|
101
|
+
- **Navigation:** Push and pop screens with argument passing via the `use_navigation()` hook.
|
|
102
|
+
- **Bundled templates:** Android Gradle and iOS Xcode templates are included, so scaffolding requires no network access.
|
|
102
103
|
|
|
103
104
|
## Quick Start
|
|
104
105
|
|
|
@@ -114,21 +115,17 @@ pip install pythonnative
|
|
|
114
115
|
import pythonnative as pn
|
|
115
116
|
|
|
116
117
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
),
|
|
129
|
-
spacing=12,
|
|
130
|
-
padding=16,
|
|
131
|
-
)
|
|
118
|
+
@pn.component
|
|
119
|
+
def MainPage():
|
|
120
|
+
count, set_count = pn.use_state(0)
|
|
121
|
+
return pn.Column(
|
|
122
|
+
pn.Text(f"Count: {count}", style={"font_size": 24}),
|
|
123
|
+
pn.Button(
|
|
124
|
+
"Tap me",
|
|
125
|
+
on_click=lambda: set_count(count + 1),
|
|
126
|
+
),
|
|
127
|
+
style={"spacing": 12, "padding": 16},
|
|
128
|
+
)
|
|
132
129
|
```
|
|
133
130
|
|
|
134
131
|
## Documentation
|
|
@@ -26,17 +26,18 @@
|
|
|
26
26
|
|
|
27
27
|
## Overview
|
|
28
28
|
|
|
29
|
-
PythonNative is a cross-platform toolkit for building native Android and iOS apps in Python. It provides a **declarative, React-like component model** with automatic reconciliation, powered by Chaquopy on Android and rubicon-objc on iOS.
|
|
29
|
+
PythonNative is a cross-platform toolkit for building native Android and iOS apps in Python. It provides a **declarative, React-like component model** with hooks and automatic reconciliation, powered by Chaquopy on Android and rubicon-objc on iOS. Write function components with `use_state`, `use_effect`, and friends, just like React, and let PythonNative handle creating and updating native views.
|
|
30
30
|
|
|
31
31
|
## Features
|
|
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
|
+
- **`style` prop:** Pass all visual and layout properties through a single `style` dict, composable via `StyleSheet`.
|
|
35
36
|
- **Virtual view tree + reconciler:** Element trees are diffed and patched with minimal native mutations, similar to React's reconciliation.
|
|
36
37
|
- **Direct native bindings:** Python calls platform APIs directly through Chaquopy and rubicon-objc, with no JavaScript bridge.
|
|
37
38
|
- **CLI scaffolding:** `pn init` creates a ready-to-run project; `pn run android` and `pn run ios` build and launch your app.
|
|
38
|
-
- **Navigation:** Push and pop screens with argument passing
|
|
39
|
-
- **Bundled templates:** Android Gradle and iOS Xcode templates are included
|
|
39
|
+
- **Navigation:** Push and pop screens with argument passing via the `use_navigation()` hook.
|
|
40
|
+
- **Bundled templates:** Android Gradle and iOS Xcode templates are included, so scaffolding requires no network access.
|
|
40
41
|
|
|
41
42
|
## Quick Start
|
|
42
43
|
|
|
@@ -52,21 +53,17 @@ pip install pythonnative
|
|
|
52
53
|
import pythonnative as pn
|
|
53
54
|
|
|
54
55
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
),
|
|
67
|
-
spacing=12,
|
|
68
|
-
padding=16,
|
|
69
|
-
)
|
|
56
|
+
@pn.component
|
|
57
|
+
def MainPage():
|
|
58
|
+
count, set_count = pn.use_state(0)
|
|
59
|
+
return pn.Column(
|
|
60
|
+
pn.Text(f"Count: {count}", style={"font_size": 24}),
|
|
61
|
+
pn.Button(
|
|
62
|
+
"Tap me",
|
|
63
|
+
on_click=lambda: set_count(count + 1),
|
|
64
|
+
),
|
|
65
|
+
style={"spacing": 12, "padding": 16},
|
|
66
|
+
)
|
|
70
67
|
```
|
|
71
68
|
|
|
72
69
|
## Documentation
|
|
@@ -5,28 +5,16 @@ Public API::
|
|
|
5
5
|
import pythonnative as pn
|
|
6
6
|
|
|
7
7
|
@pn.component
|
|
8
|
-
def
|
|
9
|
-
count, set_count = pn.use_state(
|
|
8
|
+
def App():
|
|
9
|
+
count, set_count = pn.use_state(0)
|
|
10
10
|
return pn.Column(
|
|
11
|
-
pn.Text(f"Count: {count}", font_size
|
|
11
|
+
pn.Text(f"Count: {count}", style={"font_size": 24}),
|
|
12
12
|
pn.Button("+", on_click=lambda: set_count(count + 1)),
|
|
13
|
-
spacing
|
|
13
|
+
style={"spacing": 12},
|
|
14
14
|
)
|
|
15
|
-
|
|
16
|
-
class MainPage(pn.Page):
|
|
17
|
-
def __init__(self, native_instance):
|
|
18
|
-
super().__init__(native_instance)
|
|
19
|
-
|
|
20
|
-
def render(self):
|
|
21
|
-
return pn.Column(
|
|
22
|
-
counter(initial=0),
|
|
23
|
-
counter(initial=10),
|
|
24
|
-
spacing=16,
|
|
25
|
-
padding=16,
|
|
26
|
-
)
|
|
27
15
|
"""
|
|
28
16
|
|
|
29
|
-
__version__ = "0.
|
|
17
|
+
__version__ = "0.7.0"
|
|
30
18
|
|
|
31
19
|
from .components import (
|
|
32
20
|
ActivityIndicator,
|
|
@@ -57,10 +45,11 @@ from .hooks import (
|
|
|
57
45
|
use_context,
|
|
58
46
|
use_effect,
|
|
59
47
|
use_memo,
|
|
48
|
+
use_navigation,
|
|
60
49
|
use_ref,
|
|
61
50
|
use_state,
|
|
62
51
|
)
|
|
63
|
-
from .page import
|
|
52
|
+
from .page import create_page
|
|
64
53
|
from .style import StyleSheet, ThemeContext
|
|
65
54
|
|
|
66
55
|
__all__ = [
|
|
@@ -85,7 +74,7 @@ __all__ = [
|
|
|
85
74
|
"WebView",
|
|
86
75
|
# Core
|
|
87
76
|
"Element",
|
|
88
|
-
"
|
|
77
|
+
"create_page",
|
|
89
78
|
# Hooks
|
|
90
79
|
"component",
|
|
91
80
|
"create_context",
|
|
@@ -93,6 +82,7 @@ __all__ = [
|
|
|
93
82
|
"use_context",
|
|
94
83
|
"use_effect",
|
|
95
84
|
"use_memo",
|
|
85
|
+
"use_navigation",
|
|
96
86
|
"use_ref",
|
|
97
87
|
"use_state",
|
|
98
88
|
"Provider",
|
|
@@ -48,25 +48,17 @@ def init_project(args: argparse.Namespace) -> None:
|
|
|
48
48
|
f.write("""import pythonnative as pn
|
|
49
49
|
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return pn.ScrollView(
|
|
61
|
-
pn.Column(
|
|
62
|
-
pn.Text("Hello from PythonNative!", font_size=24, bold=True),
|
|
63
|
-
pn.Text(f"Tapped {self.state['count']} times"),
|
|
64
|
-
pn.Button("Tap me", on_click=self.increment),
|
|
65
|
-
spacing=12,
|
|
66
|
-
padding=16,
|
|
67
|
-
alignment="fill",
|
|
68
|
-
)
|
|
51
|
+
@pn.component
|
|
52
|
+
def MainPage():
|
|
53
|
+
count, set_count = pn.use_state(0)
|
|
54
|
+
return pn.ScrollView(
|
|
55
|
+
pn.Column(
|
|
56
|
+
pn.Text("Hello from PythonNative!", style={"font_size": 24, "bold": True}),
|
|
57
|
+
pn.Text(f"Tapped {count} times"),
|
|
58
|
+
pn.Button("Tap me", on_click=lambda: set_count(count + 1)),
|
|
59
|
+
style={"spacing": 12, "padding": 16, "align_items": "stretch"},
|
|
69
60
|
)
|
|
61
|
+
)
|
|
70
62
|
""")
|
|
71
63
|
|
|
72
64
|
# Create config
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
"""Built-in element-creating functions for declarative UI composition.
|
|
2
|
+
|
|
3
|
+
Each function returns an :class:`Element` describing a native UI widget.
|
|
4
|
+
These are pure data — no native views are created until the reconciler
|
|
5
|
+
mounts the element tree.
|
|
6
|
+
|
|
7
|
+
All visual and layout properties are passed via the ``style`` parameter,
|
|
8
|
+
which accepts a dict or a list of dicts (later entries override earlier).
|
|
9
|
+
|
|
10
|
+
Layout properties supported by all components::
|
|
11
|
+
|
|
12
|
+
width, height, flex, margin, min_width, max_width, min_height,
|
|
13
|
+
max_height, align_self
|
|
14
|
+
|
|
15
|
+
Container-specific layout properties (Column / Row)::
|
|
16
|
+
|
|
17
|
+
spacing, padding, align_items, justify_content
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
21
|
+
|
|
22
|
+
from .element import Element
|
|
23
|
+
from .style import StyleValue, resolve_style
|
|
24
|
+
|
|
25
|
+
# ======================================================================
|
|
26
|
+
# Leaf components
|
|
27
|
+
# ======================================================================
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def Text(
|
|
31
|
+
text: str = "",
|
|
32
|
+
*,
|
|
33
|
+
style: StyleValue = None,
|
|
34
|
+
key: Optional[str] = None,
|
|
35
|
+
) -> Element:
|
|
36
|
+
"""Display text.
|
|
37
|
+
|
|
38
|
+
Style properties: ``font_size``, ``color``, ``bold``, ``text_align``,
|
|
39
|
+
``background_color``, ``max_lines``, plus common layout props.
|
|
40
|
+
"""
|
|
41
|
+
props: Dict[str, Any] = {"text": text}
|
|
42
|
+
props.update(resolve_style(style))
|
|
43
|
+
return Element("Text", props, [], key=key)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def Button(
|
|
47
|
+
title: str = "",
|
|
48
|
+
*,
|
|
49
|
+
on_click: Optional[Callable[[], None]] = None,
|
|
50
|
+
enabled: bool = True,
|
|
51
|
+
style: StyleValue = None,
|
|
52
|
+
key: Optional[str] = None,
|
|
53
|
+
) -> Element:
|
|
54
|
+
"""Create a tappable button.
|
|
55
|
+
|
|
56
|
+
Style properties: ``color``, ``background_color``, ``font_size``,
|
|
57
|
+
plus common layout props.
|
|
58
|
+
"""
|
|
59
|
+
props: Dict[str, Any] = {"title": title}
|
|
60
|
+
if on_click is not None:
|
|
61
|
+
props["on_click"] = on_click
|
|
62
|
+
if not enabled:
|
|
63
|
+
props["enabled"] = False
|
|
64
|
+
props.update(resolve_style(style))
|
|
65
|
+
return Element("Button", props, [], key=key)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def TextInput(
|
|
69
|
+
*,
|
|
70
|
+
value: str = "",
|
|
71
|
+
placeholder: str = "",
|
|
72
|
+
on_change: Optional[Callable[[str], None]] = None,
|
|
73
|
+
secure: bool = False,
|
|
74
|
+
style: StyleValue = None,
|
|
75
|
+
key: Optional[str] = None,
|
|
76
|
+
) -> Element:
|
|
77
|
+
"""Create a single-line text entry field.
|
|
78
|
+
|
|
79
|
+
Style properties: ``font_size``, ``color``, ``background_color``,
|
|
80
|
+
plus common layout props.
|
|
81
|
+
"""
|
|
82
|
+
props: Dict[str, Any] = {"value": value}
|
|
83
|
+
if placeholder:
|
|
84
|
+
props["placeholder"] = placeholder
|
|
85
|
+
if on_change is not None:
|
|
86
|
+
props["on_change"] = on_change
|
|
87
|
+
if secure:
|
|
88
|
+
props["secure"] = True
|
|
89
|
+
props.update(resolve_style(style))
|
|
90
|
+
return Element("TextInput", props, [], key=key)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def Image(
|
|
94
|
+
source: str = "",
|
|
95
|
+
*,
|
|
96
|
+
scale_type: Optional[str] = None,
|
|
97
|
+
style: StyleValue = None,
|
|
98
|
+
key: Optional[str] = None,
|
|
99
|
+
) -> Element:
|
|
100
|
+
"""Display an image from a resource path or URL.
|
|
101
|
+
|
|
102
|
+
Style properties: ``background_color``, plus common layout props.
|
|
103
|
+
"""
|
|
104
|
+
props: Dict[str, Any] = {}
|
|
105
|
+
if source:
|
|
106
|
+
props["source"] = source
|
|
107
|
+
if scale_type is not None:
|
|
108
|
+
props["scale_type"] = scale_type
|
|
109
|
+
props.update(resolve_style(style))
|
|
110
|
+
return Element("Image", props, [], key=key)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def Switch(
|
|
114
|
+
*,
|
|
115
|
+
value: bool = False,
|
|
116
|
+
on_change: Optional[Callable[[bool], None]] = None,
|
|
117
|
+
style: StyleValue = None,
|
|
118
|
+
key: Optional[str] = None,
|
|
119
|
+
) -> Element:
|
|
120
|
+
"""Create a toggle switch."""
|
|
121
|
+
props: Dict[str, Any] = {"value": value}
|
|
122
|
+
if on_change is not None:
|
|
123
|
+
props["on_change"] = on_change
|
|
124
|
+
props.update(resolve_style(style))
|
|
125
|
+
return Element("Switch", props, [], key=key)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def ProgressBar(
|
|
129
|
+
*,
|
|
130
|
+
value: float = 0.0,
|
|
131
|
+
style: StyleValue = None,
|
|
132
|
+
key: Optional[str] = None,
|
|
133
|
+
) -> Element:
|
|
134
|
+
"""Show determinate progress (0.0 – 1.0)."""
|
|
135
|
+
props: Dict[str, Any] = {"value": value}
|
|
136
|
+
props.update(resolve_style(style))
|
|
137
|
+
return Element("ProgressBar", props, [], key=key)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def ActivityIndicator(
|
|
141
|
+
*,
|
|
142
|
+
animating: bool = True,
|
|
143
|
+
style: StyleValue = None,
|
|
144
|
+
key: Optional[str] = None,
|
|
145
|
+
) -> Element:
|
|
146
|
+
"""Show an indeterminate loading spinner."""
|
|
147
|
+
props: Dict[str, Any] = {"animating": animating}
|
|
148
|
+
props.update(resolve_style(style))
|
|
149
|
+
return Element("ActivityIndicator", props, [], key=key)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def WebView(
|
|
153
|
+
*,
|
|
154
|
+
url: str = "",
|
|
155
|
+
style: StyleValue = None,
|
|
156
|
+
key: Optional[str] = None,
|
|
157
|
+
) -> Element:
|
|
158
|
+
"""Embed web content."""
|
|
159
|
+
props: Dict[str, Any] = {}
|
|
160
|
+
if url:
|
|
161
|
+
props["url"] = url
|
|
162
|
+
props.update(resolve_style(style))
|
|
163
|
+
return Element("WebView", props, [], key=key)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def Spacer(
|
|
167
|
+
*,
|
|
168
|
+
size: Optional[float] = None,
|
|
169
|
+
flex: Optional[float] = None,
|
|
170
|
+
key: Optional[str] = None,
|
|
171
|
+
) -> Element:
|
|
172
|
+
"""Insert empty space with an optional fixed size or flex weight."""
|
|
173
|
+
props: Dict[str, Any] = {}
|
|
174
|
+
if size is not None:
|
|
175
|
+
props["size"] = size
|
|
176
|
+
if flex is not None:
|
|
177
|
+
props["flex"] = flex
|
|
178
|
+
return Element("Spacer", props, [], key=key)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def Slider(
|
|
182
|
+
*,
|
|
183
|
+
value: float = 0.0,
|
|
184
|
+
min_value: float = 0.0,
|
|
185
|
+
max_value: float = 1.0,
|
|
186
|
+
on_change: Optional[Callable[[float], None]] = None,
|
|
187
|
+
style: StyleValue = None,
|
|
188
|
+
key: Optional[str] = None,
|
|
189
|
+
) -> Element:
|
|
190
|
+
"""Continuous value slider."""
|
|
191
|
+
props: Dict[str, Any] = {
|
|
192
|
+
"value": value,
|
|
193
|
+
"min_value": min_value,
|
|
194
|
+
"max_value": max_value,
|
|
195
|
+
}
|
|
196
|
+
if on_change is not None:
|
|
197
|
+
props["on_change"] = on_change
|
|
198
|
+
props.update(resolve_style(style))
|
|
199
|
+
return Element("Slider", props, [], key=key)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
# ======================================================================
|
|
203
|
+
# Container components
|
|
204
|
+
# ======================================================================
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def Column(
|
|
208
|
+
*children: Element,
|
|
209
|
+
style: StyleValue = None,
|
|
210
|
+
key: Optional[str] = None,
|
|
211
|
+
) -> Element:
|
|
212
|
+
"""Arrange children vertically.
|
|
213
|
+
|
|
214
|
+
Style properties: ``spacing``, ``padding``, ``align_items``,
|
|
215
|
+
``justify_content``, ``background_color``, plus common layout props.
|
|
216
|
+
|
|
217
|
+
``align_items`` controls cross-axis (horizontal) alignment:
|
|
218
|
+
``"stretch"`` (default), ``"flex_start"``/``"leading"``,
|
|
219
|
+
``"center"``, ``"flex_end"``/``"trailing"``.
|
|
220
|
+
|
|
221
|
+
``justify_content`` controls main-axis (vertical) distribution:
|
|
222
|
+
``"flex_start"`` (default), ``"center"``, ``"flex_end"``,
|
|
223
|
+
``"space_between"``, ``"space_around"``, ``"space_evenly"``.
|
|
224
|
+
"""
|
|
225
|
+
props: Dict[str, Any] = {}
|
|
226
|
+
props.update(resolve_style(style))
|
|
227
|
+
return Element("Column", props, list(children), key=key)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def Row(
|
|
231
|
+
*children: Element,
|
|
232
|
+
style: StyleValue = None,
|
|
233
|
+
key: Optional[str] = None,
|
|
234
|
+
) -> Element:
|
|
235
|
+
"""Arrange children horizontally.
|
|
236
|
+
|
|
237
|
+
Style properties: ``spacing``, ``padding``, ``align_items``,
|
|
238
|
+
``justify_content``, ``background_color``, plus common layout props.
|
|
239
|
+
|
|
240
|
+
``align_items`` controls cross-axis (vertical) alignment:
|
|
241
|
+
``"stretch"`` (default), ``"flex_start"``/``"top"``,
|
|
242
|
+
``"center"``, ``"flex_end"``/``"bottom"``.
|
|
243
|
+
|
|
244
|
+
``justify_content`` controls main-axis (horizontal) distribution:
|
|
245
|
+
``"flex_start"`` (default), ``"center"``, ``"flex_end"``,
|
|
246
|
+
``"space_between"``, ``"space_around"``, ``"space_evenly"``.
|
|
247
|
+
"""
|
|
248
|
+
props: Dict[str, Any] = {}
|
|
249
|
+
props.update(resolve_style(style))
|
|
250
|
+
return Element("Row", props, list(children), key=key)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def ScrollView(
|
|
254
|
+
child: Optional[Element] = None,
|
|
255
|
+
*,
|
|
256
|
+
style: StyleValue = None,
|
|
257
|
+
key: Optional[str] = None,
|
|
258
|
+
) -> Element:
|
|
259
|
+
"""Wrap a single child in a scrollable container."""
|
|
260
|
+
children = [child] if child is not None else []
|
|
261
|
+
props: Dict[str, Any] = {}
|
|
262
|
+
props.update(resolve_style(style))
|
|
263
|
+
return Element("ScrollView", props, children, key=key)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def View(
|
|
267
|
+
*children: Element,
|
|
268
|
+
style: StyleValue = None,
|
|
269
|
+
key: Optional[str] = None,
|
|
270
|
+
) -> Element:
|
|
271
|
+
"""Generic container view (``UIView`` / ``android.view.View``)."""
|
|
272
|
+
props: Dict[str, Any] = {}
|
|
273
|
+
props.update(resolve_style(style))
|
|
274
|
+
return Element("View", props, list(children), key=key)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def SafeAreaView(
|
|
278
|
+
*children: Element,
|
|
279
|
+
style: StyleValue = None,
|
|
280
|
+
key: Optional[str] = None,
|
|
281
|
+
) -> Element:
|
|
282
|
+
"""Container that respects safe area insets (notch, status bar)."""
|
|
283
|
+
props: Dict[str, Any] = {}
|
|
284
|
+
props.update(resolve_style(style))
|
|
285
|
+
return Element("SafeAreaView", props, list(children), key=key)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def Modal(
|
|
289
|
+
*children: Element,
|
|
290
|
+
visible: bool = False,
|
|
291
|
+
on_dismiss: Optional[Callable[[], None]] = None,
|
|
292
|
+
title: Optional[str] = None,
|
|
293
|
+
style: StyleValue = None,
|
|
294
|
+
key: Optional[str] = None,
|
|
295
|
+
) -> Element:
|
|
296
|
+
"""Overlay modal dialog.
|
|
297
|
+
|
|
298
|
+
The modal is shown when ``visible=True`` and hidden when ``False``.
|
|
299
|
+
"""
|
|
300
|
+
props: Dict[str, Any] = {"visible": visible}
|
|
301
|
+
if on_dismiss is not None:
|
|
302
|
+
props["on_dismiss"] = on_dismiss
|
|
303
|
+
if title is not None:
|
|
304
|
+
props["title"] = title
|
|
305
|
+
props.update(resolve_style(style))
|
|
306
|
+
return Element("Modal", props, list(children), key=key)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def Pressable(
|
|
310
|
+
child: Optional[Element] = None,
|
|
311
|
+
*,
|
|
312
|
+
on_press: Optional[Callable[[], None]] = None,
|
|
313
|
+
on_long_press: Optional[Callable[[], None]] = None,
|
|
314
|
+
key: Optional[str] = None,
|
|
315
|
+
) -> Element:
|
|
316
|
+
"""Wrapper that adds press handling to any child element."""
|
|
317
|
+
props: Dict[str, Any] = {}
|
|
318
|
+
if on_press is not None:
|
|
319
|
+
props["on_press"] = on_press
|
|
320
|
+
if on_long_press is not None:
|
|
321
|
+
props["on_long_press"] = on_long_press
|
|
322
|
+
children = [child] if child is not None else []
|
|
323
|
+
return Element("Pressable", props, children, key=key)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def FlatList(
|
|
327
|
+
*,
|
|
328
|
+
data: Optional[List[Any]] = None,
|
|
329
|
+
render_item: Optional[Callable[[Any, int], Element]] = None,
|
|
330
|
+
key_extractor: Optional[Callable[[Any, int], str]] = None,
|
|
331
|
+
separator_height: float = 0,
|
|
332
|
+
style: StyleValue = None,
|
|
333
|
+
key: Optional[str] = None,
|
|
334
|
+
) -> Element:
|
|
335
|
+
"""Scrollable list that renders items from *data* using *render_item*.
|
|
336
|
+
|
|
337
|
+
Each item is rendered by calling ``render_item(item, index)``. If
|
|
338
|
+
``key_extractor`` is provided, it is called as ``key_extractor(item, index)``
|
|
339
|
+
to produce a stable key for each child element.
|
|
340
|
+
"""
|
|
341
|
+
items: List[Element] = []
|
|
342
|
+
for i, item in enumerate(data or []):
|
|
343
|
+
el = render_item(item, i) if render_item else Text(str(item))
|
|
344
|
+
if key_extractor is not None:
|
|
345
|
+
el = Element(el.type, el.props, el.children, key=key_extractor(item, i))
|
|
346
|
+
items.append(el)
|
|
347
|
+
|
|
348
|
+
inner = Column(*items, style={"spacing": separator_height} if separator_height else None)
|
|
349
|
+
sv_props: Dict[str, Any] = {}
|
|
350
|
+
sv_props.update(resolve_style(style))
|
|
351
|
+
return Element("ScrollView", sv_props, [inner], key=key)
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""Hook primitives for function components.
|
|
2
2
|
|
|
3
3
|
Provides React-like hooks for managing state, effects, memoisation,
|
|
4
|
-
and
|
|
4
|
+
context, and navigation within function components decorated with
|
|
5
|
+
:func:`component`.
|
|
5
6
|
|
|
6
7
|
Usage::
|
|
7
8
|
|
|
@@ -18,7 +19,7 @@ Usage::
|
|
|
18
19
|
|
|
19
20
|
import inspect
|
|
20
21
|
import threading
|
|
21
|
-
from typing import Any, Callable, List, Optional, Tuple, TypeVar
|
|
22
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple, TypeVar
|
|
22
23
|
|
|
23
24
|
from .element import Element
|
|
24
25
|
|
|
@@ -246,6 +247,52 @@ def Provider(context: Context, value: Any, child: Element) -> Element:
|
|
|
246
247
|
return Element("__Provider__", {"__context__": context, "__value__": value}, [child])
|
|
247
248
|
|
|
248
249
|
|
|
250
|
+
# ======================================================================
|
|
251
|
+
# Navigation
|
|
252
|
+
# ======================================================================
|
|
253
|
+
|
|
254
|
+
_NavigationContext: Context = create_context(None)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class NavigationHandle:
|
|
258
|
+
"""Object returned by :func:`use_navigation` providing push/pop/get_args.
|
|
259
|
+
|
|
260
|
+
Navigates by component reference rather than string path, e.g.::
|
|
261
|
+
|
|
262
|
+
nav = pn.use_navigation()
|
|
263
|
+
nav.push(DetailScreen, args={"id": 42})
|
|
264
|
+
"""
|
|
265
|
+
|
|
266
|
+
def __init__(self, host: Any) -> None:
|
|
267
|
+
self._host = host
|
|
268
|
+
|
|
269
|
+
def push(self, page: Any, args: Optional[Dict[str, Any]] = None) -> None:
|
|
270
|
+
"""Navigate forward to *page* (a ``@component`` function or class)."""
|
|
271
|
+
self._host._push(page, args)
|
|
272
|
+
|
|
273
|
+
def pop(self) -> None:
|
|
274
|
+
"""Navigate back to the previous screen."""
|
|
275
|
+
self._host._pop()
|
|
276
|
+
|
|
277
|
+
def get_args(self) -> Dict[str, Any]:
|
|
278
|
+
"""Return arguments passed from the previous screen."""
|
|
279
|
+
return self._host._get_nav_args()
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def use_navigation() -> NavigationHandle:
|
|
283
|
+
"""Return a :class:`NavigationHandle` for the current screen.
|
|
284
|
+
|
|
285
|
+
Must be called inside a ``@component`` function rendered by PythonNative.
|
|
286
|
+
"""
|
|
287
|
+
handle = use_context(_NavigationContext)
|
|
288
|
+
if handle is None:
|
|
289
|
+
raise RuntimeError(
|
|
290
|
+
"use_navigation() called outside a PythonNative page. "
|
|
291
|
+
"Ensure your component is rendered via create_page()."
|
|
292
|
+
)
|
|
293
|
+
return handle
|
|
294
|
+
|
|
295
|
+
|
|
249
296
|
# ======================================================================
|
|
250
297
|
# @component decorator
|
|
251
298
|
# ======================================================================
|