Skip to content

Commit 06ea22d

Browse files
committed
feat(core,templates)!: add push/pop navigation and lifecycle wiring
1 parent 6d61ffc commit 06ea22d

File tree

14 files changed

+552
-73
lines changed

14 files changed

+552
-73
lines changed

docs/concepts/architecture.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ PythonNative maps Python directly to native platform APIs. Conceptually, it is c
1717

1818
## iOS flow (Rubicon-ObjC)
1919

20-
- The iOS template (Swift + PythonKit) boots Python and calls your bootstrap(native_instance) with the UIViewController pointer.
20+
- The iOS template (Swift + PythonKit) boots Python and instantiates your `MainPage` with the current `UIViewController` pointer.
2121
- In Python, Rubicon wraps the pointer; you then interact with UIKit classes directly.
2222

2323
```python

docs/getting-started.md

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ This scaffolds:
1818
- `requirements.txt`
1919
- `.gitignore`
2020

21-
A minimal `app/main_page.py` looks like:
21+
A minimal `app/main_page.py` looks like (no bootstrap needed):
2222

2323
```python
2424
import pythonnative as pn
@@ -36,13 +36,6 @@ class MainPage(pn.Page):
3636
button.set_on_click(lambda: print("Button clicked"))
3737
stack.add_view(button)
3838
self.set_root_view(stack)
39-
40-
41-
def bootstrap(native_instance):
42-
"""Entry point called by the host app (Activity or ViewController)."""
43-
page = MainPage(native_instance)
44-
page.on_create()
45-
return page
4639
```
4740

4841
## Run on a platform

docs/guides/navigation.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Navigation
2+
3+
This guide shows how to navigate between pages and handle lifecycle events.
4+
5+
## Push / Pop
6+
7+
Use `push` and `pop` on your `Page` to change screens. You can pass a dotted path string or a class reference.
8+
9+
```python
10+
import pythonnative as pn
11+
12+
class MainPage(pn.Page):
13+
def on_create(self):
14+
stack = pn.StackView()
15+
btn = pn.Button("Go next")
16+
btn.set_on_click(lambda: self.push("app.second_page.SecondPage", args={"message": "Hello"}))
17+
stack.add_view(btn)
18+
self.set_root_view(stack)
19+
```
20+
21+
On the target page:
22+
23+
```python
24+
class SecondPage(pn.Page):
25+
def on_create(self):
26+
args = self.get_args()
27+
message = args.get("message", "Second")
28+
stack = pn.StackView()
29+
stack.add_view(pn.Label(message))
30+
back = pn.Button("Back")
31+
back.set_on_click(lambda: self.pop())
32+
stack.add_view(back)
33+
self.set_root_view(stack)
34+
```
35+
36+
## Lifecycle
37+
38+
PythonNative forwards lifecycle events from the host:
39+
40+
- `on_create`
41+
- `on_start`
42+
- `on_resume`
43+
- `on_pause`
44+
- `on_stop`
45+
- `on_destroy`
46+
- `on_restart` (Android only)
47+
- `on_save_instance_state`
48+
- `on_restore_instance_state`
49+
50+
Android forwards Activity lifecycle via the template `MainActivity` and `PageActivity`. iOS forwards `viewWillAppear`/`viewWillDisappear` via an internal registry.
51+
52+
## Notes
53+
54+
- On Android, `push` launches a template `PageActivity` and passes `PY_PAGE_PATH` and optional JSON args.
55+
- On iOS, `push` uses the root `UINavigationController` to push a new `ViewController` and passes page info via KVC.

examples/hello-world/app/main_page.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,20 @@ def on_create(self):
2222
except Exception:
2323
pass
2424
stack.add_view(pn.Label("Hello from PythonNative Demo!"))
25-
button = pn.Button("Tap me")
26-
button.set_on_click(lambda: print("Demo button clicked"))
25+
button = pn.Button("Go to Second Page")
26+
27+
def on_next():
28+
# Visual confirmation that tap worked (iOS only)
29+
try:
30+
if UIColor is not None:
31+
button.native_instance.setBackgroundColor_(UIColor.systemGreenColor())
32+
button.native_instance.setTitleColor_forState_(UIColor.whiteColor(), 0)
33+
except Exception:
34+
pass
35+
# Demonstrate passing args
36+
self.push("app.second_page.SecondPage", args={"message": "Greetings from MainPage"})
37+
38+
button.set_on_click(on_next)
2739
# Make the button visually obvious
2840
try:
2941
if UIColor is not None:
@@ -57,9 +69,3 @@ def on_save_instance_state(self):
5769

5870
def on_restore_instance_state(self):
5971
super().on_restore_instance_state()
60-
61-
62-
def bootstrap(native_instance):
63-
page = MainPage(native_instance)
64-
page.on_create()
65-
return page

examples/hello-world/app/second_page.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,13 @@ def __init__(self, native_instance):
88
def on_create(self):
99
super().on_create()
1010
stack_view = pn.StackView()
11-
label = pn.Label("Second page!")
12-
stack_view.add_view(label)
11+
# Read args passed from MainPage
12+
args = self.get_args()
13+
message = args.get("message", "Second page!")
14+
stack_view.add_view(pn.Label(message))
15+
back_btn = pn.Button("Back")
16+
back_btn.set_on_click(lambda: self.pop())
17+
stack_view.add_view(back_btn)
1318
self.set_root_view(stack_view)
1419

1520
def on_start(self):

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ nav:
2323
- Guides:
2424
- Android: guides/android.md
2525
- iOS: guides/ios.md
26+
- Navigation: guides/navigation.md
2627
- API Reference:
2728
- Package: api/pythonnative.md
2829
- Meta:

src/pythonnative/cli/pn.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def init_project(args: argparse.Namespace) -> None:
4141

4242
os.makedirs(app_dir, exist_ok=True)
4343

44-
# Minimal hello world app scaffold
44+
# Minimal hello world app scaffold (no bootstrap function; host instantiates Page directly)
4545
main_page_py = os.path.join(app_dir, "main_page.py")
4646
if not os.path.exists(main_page_py) or args.force:
4747
with open(main_page_py, "w", encoding="utf-8") as f:
@@ -61,13 +61,6 @@ def on_create(self):
6161
button.set_on_click(lambda: print("Button clicked"))
6262
stack.add_view(button)
6363
self.set_root_view(stack)
64-
65-
66-
def bootstrap(native_instance):
67-
'''Entry point called by the host app (Android Activity or iOS ViewController).'''
68-
page = MainPage(native_instance)
69-
page.on_create()
70-
return page
7164
"""
7265
)
7366

0 commit comments

Comments
 (0)