FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

feat!: overhaul native bridge, animations, gestures, and lists · pythonnative/pythonnative@dbc4d70 · GitHub

Commit dbc4d70

Browse files
committed
feat!: overhaul native bridge, animations, gestures, and lists
1 parent 7cab301 commit dbc4d70

67 files changed

Lines changed: 9173 additions & 5502 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CONTRIBUTING.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,12 @@ Recommended scopes (choose the smallest, most accurate unit; prefer module/direc
104104
- `cli` – CLI tool and `pn` command (`src/pythonnative/cli/`)
105105
- `components` – declarative element-creating functions (`components.py`)
106106
- `element` – Element descriptor class (`element.py`)
107+
- `events` – tag-based event routing between native views and Python callbacks (`events.py`)
108+
- `gestures` – gesture descriptors and the pure-Python recognition arbiter (`gestures.py`)
107109
- `hooks` – function components and hooks (`hooks.py`)
108110
- `hot_reload` – file watcher and module reloader (`hot_reload.py`)
109111
- `layout` – pure-Python flexbox engine (`layout.py`)
112+
- `mutations` – batched mutation ops between reconciler and native backends (`mutations.py`)
110113
- `native_modules` – native API modules for device capabilities (`native_modules/`)
111114
- `native_views` – platform-specific native view creation and updates (`native_views/`)
112115
- `navigation` – navigation containers and stack/tab/drawer navigators (`navigation.py`)

‎README.md‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ PythonNative is a cross-platform toolkit for building native Android and iOS app
3636
- **Hooks and function components:** Manage state with `use_state`, side effects with `use_effect`, and navigation with `use_navigation`, all through one consistent pattern.
3737
- **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`.
3838
- **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.
39-
- **Virtual view tree + reconciler:** Element trees are diffed and patched with minimal native mutations, similar to React's reconciliation. State updates re-render **locally** — only the component whose state changed (and its subtree) re-runs, and unchanged leaves reuse cached intrinsic measurements — so deep UIs stay responsive instead of re-rendering the whole app from the root on every tap.
39+
- **Virtual view tree + reconciler:** Element trees are diffed and patched with minimal native mutations, similar to React's reconciliation. Each commit lands as **one batched transaction** of mutation ops, and event callbacks are routed through a tag-based registry so re-renders that only change closures cost zero native calls. State updates re-render **locally** — only the component whose state changed (and its subtree) re-runs, and unchanged leaves reuse cached intrinsic measurements — so deep UIs stay responsive instead of re-rendering the whole app from the root on every tap.
40+
- **Native-driven animations:** The `Animated` API (timing / spring / decay, awaitable or fire-and-forget) hands animations to Core Animation and `ViewPropertyAnimator` whenever possible, so no Python code runs per frame; a pure-Python ticker covers the rest.
41+
- **Native gesture system:** Attach `Tap`, `LongPress`, `Pan`, `Swipe`, `Pinch`, and `Rotation` recognizers to any view via the `gestures=` prop — backed by `UIGestureRecognizer` on iOS and a unit-testable pure-Python arbiter on Android and desktop.
42+
- **Virtualized lists:** `FlatList` / `SectionList` window their rows in Python over the platform scroll view — uniform, exact, or measured variable heights, grids, headers/footers, infinite scroll, and an imperative scroll controller, identical on every platform.
4043
- **Direct native bindings:** Python calls platform APIs directly through Chaquopy and rubicon-objc, with no JavaScript bridge.
4144
- **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.
4245
- **CLI scaffolding:** `pn init` creates a ready-to-run project; `pn run android` and `pn run ios` build and launch your app.

‎docs/api/gestures.md‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Gestures
2+
3+
Native-backed gesture recognition, attached to any view-like element
4+
via the `gestures=` prop. Descriptors are frozen dataclasses; their
5+
numeric configuration crosses the bridge while callbacks are routed
6+
through the tag-based event channel. See the
7+
[Gestures guide](../guides/gestures.md) for usage patterns.
8+
9+
::: pythonnative.gestures
10+
options:
11+
show_root_heading: false
12+
show_root_toc_entry: false
13+
members_order: source
14+
filters: ["!^_"]
15+
show_if_no_docstring: true
16+
17+
## See also
18+
19+
- The [Gestures guide](../guides/gestures.md) walks through taps,
20+
drags, and gesture-driven animations.
21+
- [Animated](animated.md) pairs with `Pan` velocity for springs and
22+
decays.

‎docs/api/native_views.md‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
# Native views
22

33
The bridge between PythonNative's element tree and concrete native
4-
widgets. Every element type maps to a
4+
widgets. Each commit's diff is expressed as a flat list of mutation
5+
ops referencing integer tags, applied through a single
6+
[`apply_mutations`][pythonnative.native_views.NativeViewRegistry.apply_mutations]
7+
call. Every element type maps to a
58
[`ViewHandler`][pythonnative.native_views.base.ViewHandler]
69
implementation in the
710
[`NativeViewRegistry`][pythonnative.native_views.NativeViewRegistry];
@@ -15,6 +18,24 @@ the platform-specific handlers are registered lazily so importing
1518
members_order: source
1619
filters: ["!^_"]
1720

21+
## Mutation ops
22+
23+
::: pythonnative.mutations
24+
options:
25+
show_root_heading: false
26+
show_root_toc_entry: false
27+
members_order: source
28+
filters: ["!^_"]
29+
30+
## Event routing
31+
32+
::: pythonnative.events
33+
options:
34+
show_root_heading: false
35+
show_root_toc_entry: false
36+
members_order: source
37+
filters: ["!^_"]
38+
1839
## Base classes
1940

2041
::: pythonnative.native_views.base

‎docs/concepts/architecture.md‎

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,12 @@ platform APIs synchronously from Python.
1919
3. **Reconciler.** On first render, the
2020
[`Reconciler`][pythonnative.reconciler.Reconciler] walks the tree
2121
and creates real native views via the platform backend. On
22-
subsequent renders it diffs against the previous tree and applies
23-
the minimal set of native mutations. State-driven renders are
22+
subsequent renders it diffs against the previous tree and emits the
23+
minimal list of mutation ops (create / update / insert / remove /
24+
destroy / set-frame), applied as **one batched transaction** per
25+
commit through
26+
[`apply_mutations`][pythonnative.native_views.NativeViewRegistry.apply_mutations].
27+
State-driven renders are
2428
**local**: a setter marks only its own component subtree dirty, and
2529
[`flush_dirty`][pythonnative.reconciler.Reconciler.flush_dirty]
2630
re-runs just those components instead of the whole app from the
@@ -95,11 +99,14 @@ and a new one is created.
9599
so [`use_context`][pythonnative.use_context] still resolves. Hooks
96100
record state reads, queue effects, and register memos. No native
97101
mutations happen yet.
98-
3. **Commit phase**: the reconciler applies the diff for each
99-
re-rendered subtree to native views, creating, updating, and
100-
removing views as needed, and bubbles any swapped subtree-root view
101-
up to its nearest native container.
102-
4. **Layout phase**: a layout pass recomputes frames. Leaves whose
102+
3. **Commit phase**: the reconciler turns the diff for each
103+
re-rendered subtree into mutation ops referencing view tags, and
104+
flushes them all in a single `apply_mutations` transaction. Event
105+
callbacks are routed to the Python-side
106+
[`EventRegistry`][pythonnative.events.EventRegistry] instead of
107+
crossing the bridge, so callback-identity churn costs nothing.
108+
4. **Layout phase**: a layout pass recomputes frames and emits
109+
`SetFrameOp`s only for frames that changed. Leaves whose
103110
`Element` is unchanged reuse a cached intrinsic measurement, so
104111
untouched subtrees skip native `measure_intrinsic` calls.
105112
5. **Effect phase**: pending effects are flushed in depth-first order
@@ -256,9 +263,18 @@ Each handler class maps an element type name (e.g., `"Text"`,
256263
`"Button"`) to platform-native widget creation, property updates, and
257264
child management. The
258265
[`NativeViewRegistry`][pythonnative.native_views.NativeViewRegistry]
266+
owns the tag-to-view table, applies each commit's mutation list, and
259267
lazily imports only the relevant platform module at runtime, so the
260268
package can be imported on any platform for testing.
261269

270+
Native events flow back through a single channel: handlers wire their
271+
platform listeners once at view creation and call
272+
[`dispatch_event`][pythonnative.events.dispatch_event] with the view's
273+
tag; the [`EventRegistry`][pythonnative.events.EventRegistry] resolves
274+
the current Python callback. Gestures
275+
([`pythonnative.gestures`](../api/gestures.md)) and natively-driven
276+
animations (the `Animated` API) ride the same tag infrastructure.
277+
262278
## Comparisons
263279

264280
!!! note "Versus React Native"

‎docs/concepts/components.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,9 @@ pn.Column(
8484
**Lists:**
8585

8686
- [`FlatList(data, render_item, key_extractor, item_height, ...)`][pythonnative.FlatList]:
87-
scrollable data list. Pass `item_height=` to enable native
88-
virtualization (`UITableView` / `RecyclerView`); rows are mounted
89-
lazily as they scroll into view.
87+
virtualized scrollable data list. Rows are mounted lazily as they
88+
scroll into view; pass `item_height=` (or `get_item_height=`) for
89+
exact extents, or let rows be measured on screen.
9090
- [`SectionList(sections, render_item, render_section_header, item_height, ...)`][pythonnative.SectionList]:
9191
virtualized list with section headers.
9292

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL