pythonnative 0.22.1__py3-none-any.whl → 0.23.0__py3-none-any.whl
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/__init__.py +16 -2
- pythonnative/appearance.py +124 -0
- pythonnative/components.py +708 -38
- pythonnative/hooks.py +41 -0
- pythonnative/images.py +196 -0
- pythonnative/mutations.py +3 -12
- pythonnative/native_views/__init__.py +1 -7
- pythonnative/native_views/android.py +573 -45
- pythonnative/native_views/desktop.py +78 -19
- pythonnative/native_views/ios.py +739 -51
- pythonnative/preview.py +31 -0
- pythonnative/project/config.py +1 -9
- pythonnative/screen.py +85 -0
- pythonnative/style.py +65 -6
- pythonnative/templates/android_template/app/src/main/AndroidManifest.xml +2 -1
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNAccessibilityDelegate.kt +47 -0
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNBorderDrawable.kt +67 -0
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNVirtualListView.java +172 -0
- pythonnative/virtual_rows.py +137 -0
- {pythonnative-0.22.1.dist-info → pythonnative-0.23.0.dist-info}/METADATA +1 -1
- {pythonnative-0.22.1.dist-info → pythonnative-0.23.0.dist-info}/RECORD +25 -19
- {pythonnative-0.22.1.dist-info → pythonnative-0.23.0.dist-info}/WHEEL +1 -1
- {pythonnative-0.22.1.dist-info → pythonnative-0.23.0.dist-info}/entry_points.txt +0 -0
- {pythonnative-0.22.1.dist-info → pythonnative-0.23.0.dist-info}/licenses/LICENSE +0 -0
- {pythonnative-0.22.1.dist-info → pythonnative-0.23.0.dist-info}/top_level.txt +0 -0
pythonnative/components.py
CHANGED
|
@@ -26,10 +26,17 @@ Example:
|
|
|
26
26
|
|
|
27
27
|
import bisect
|
|
28
28
|
from dataclasses import dataclass, field
|
|
29
|
-
from typing import Any, Callable, Dict, List, Literal, Optional, Tuple
|
|
29
|
+
from typing import Any, Callable, Dict, List, Literal, Optional, Tuple, Union
|
|
30
30
|
|
|
31
31
|
from .element import Element
|
|
32
|
-
from .hooks import
|
|
32
|
+
from .hooks import (
|
|
33
|
+
component,
|
|
34
|
+
use_effect,
|
|
35
|
+
use_keyboard_height,
|
|
36
|
+
use_ref,
|
|
37
|
+
use_safe_area_insets,
|
|
38
|
+
use_state,
|
|
39
|
+
)
|
|
33
40
|
from .sdk import Props
|
|
34
41
|
from .style import (
|
|
35
42
|
AutoCapitalize,
|
|
@@ -117,10 +124,14 @@ class TextProps(Props):
|
|
|
117
124
|
"""Props for [`Text`][pythonnative.Text]."""
|
|
118
125
|
|
|
119
126
|
text: str = ""
|
|
127
|
+
spans: Optional[List[Dict[str, Any]]] = None
|
|
120
128
|
accessibility_label: Optional[str] = None
|
|
121
129
|
accessibility_hint: Optional[str] = None
|
|
122
130
|
accessibility_role: Optional[str] = None
|
|
123
131
|
accessible: Optional[bool] = None
|
|
132
|
+
accessibility_state: Optional[Dict[str, Any]] = None
|
|
133
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None
|
|
134
|
+
test_id: Optional[str] = None
|
|
124
135
|
|
|
125
136
|
|
|
126
137
|
@dataclass(frozen=True)
|
|
@@ -134,6 +145,9 @@ class ButtonProps(Props):
|
|
|
134
145
|
accessibility_hint: Optional[str] = None
|
|
135
146
|
accessibility_role: Optional[str] = None
|
|
136
147
|
accessible: Optional[bool] = None
|
|
148
|
+
accessibility_state: Optional[Dict[str, Any]] = None
|
|
149
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None
|
|
150
|
+
test_id: Optional[str] = None
|
|
137
151
|
|
|
138
152
|
|
|
139
153
|
@dataclass(frozen=True)
|
|
@@ -162,6 +176,9 @@ class TextInputProps(Props):
|
|
|
162
176
|
accessibility_label: Optional[str] = None
|
|
163
177
|
accessibility_hint: Optional[str] = None
|
|
164
178
|
accessible: Optional[bool] = None
|
|
179
|
+
accessibility_state: Optional[Dict[str, Any]] = None
|
|
180
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None
|
|
181
|
+
test_id: Optional[str] = None
|
|
165
182
|
|
|
166
183
|
|
|
167
184
|
@dataclass(frozen=True)
|
|
@@ -171,9 +188,15 @@ class ImageProps(Props):
|
|
|
171
188
|
source: Optional[str] = None
|
|
172
189
|
scale_type: Optional[ScaleType] = None
|
|
173
190
|
tint_color: Optional[Color] = None
|
|
191
|
+
placeholder_color: Optional[Color] = None
|
|
192
|
+
on_load: Optional[Callable[[], None]] = None
|
|
193
|
+
on_error: Optional[Callable[[str], None]] = None
|
|
174
194
|
accessibility_label: Optional[str] = None
|
|
175
195
|
accessibility_role: Optional[str] = None
|
|
176
196
|
accessible: Optional[bool] = None
|
|
197
|
+
accessibility_state: Optional[Dict[str, Any]] = None
|
|
198
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None
|
|
199
|
+
test_id: Optional[str] = None
|
|
177
200
|
|
|
178
201
|
|
|
179
202
|
@dataclass(frozen=True)
|
|
@@ -245,6 +268,9 @@ class ViewProps(Props):
|
|
|
245
268
|
accessibility_hint: Optional[str] = None
|
|
246
269
|
accessibility_role: Optional[str] = None
|
|
247
270
|
accessible: Optional[bool] = None
|
|
271
|
+
accessibility_state: Optional[Dict[str, Any]] = None
|
|
272
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None
|
|
273
|
+
test_id: Optional[str] = None
|
|
248
274
|
|
|
249
275
|
|
|
250
276
|
@dataclass(frozen=True)
|
|
@@ -269,6 +295,8 @@ class ScrollViewProps(Props):
|
|
|
269
295
|
class SafeAreaViewProps(Props):
|
|
270
296
|
"""Props for [`SafeAreaView`][pythonnative.SafeAreaView]."""
|
|
271
297
|
|
|
298
|
+
edges: Optional[Tuple[Literal["top", "left", "bottom", "right"], ...]] = None
|
|
299
|
+
|
|
272
300
|
|
|
273
301
|
@dataclass(frozen=True)
|
|
274
302
|
class ModalProps(Props):
|
|
@@ -298,6 +326,9 @@ class PressableProps(Props):
|
|
|
298
326
|
accessibility_hint: Optional[str] = None
|
|
299
327
|
accessibility_role: Optional[str] = None
|
|
300
328
|
accessible: Optional[bool] = None
|
|
329
|
+
accessibility_state: Optional[Dict[str, Any]] = None
|
|
330
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None
|
|
331
|
+
test_id: Optional[str] = None
|
|
301
332
|
|
|
302
333
|
|
|
303
334
|
@dataclass(frozen=True)
|
|
@@ -314,6 +345,7 @@ class KeyboardAvoidingViewProps(Props):
|
|
|
314
345
|
"""Props for [`KeyboardAvoidingView`][pythonnative.KeyboardAvoidingView]."""
|
|
315
346
|
|
|
316
347
|
behavior: Literal["padding", "position"] = "padding"
|
|
348
|
+
keyboard_vertical_offset: float = 0.0
|
|
317
349
|
|
|
318
350
|
|
|
319
351
|
@dataclass(frozen=True)
|
|
@@ -332,6 +364,9 @@ class PickerProps(Props):
|
|
|
332
364
|
accessibility_label: Optional[str] = None
|
|
333
365
|
accessibility_hint: Optional[str] = None
|
|
334
366
|
accessible: Optional[bool] = None
|
|
367
|
+
accessibility_state: Optional[Dict[str, Any]] = None
|
|
368
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None
|
|
369
|
+
test_id: Optional[str] = None
|
|
335
370
|
|
|
336
371
|
|
|
337
372
|
@dataclass(frozen=True)
|
|
@@ -346,6 +381,9 @@ class TouchableOpacityProps(Props):
|
|
|
346
381
|
accessibility_hint: Optional[str] = None
|
|
347
382
|
accessibility_role: Optional[str] = None
|
|
348
383
|
accessible: Optional[bool] = None
|
|
384
|
+
accessibility_state: Optional[Dict[str, Any]] = None
|
|
385
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None
|
|
386
|
+
test_id: Optional[str] = None
|
|
349
387
|
|
|
350
388
|
|
|
351
389
|
@dataclass(frozen=True)
|
|
@@ -356,6 +394,9 @@ class ImageBackgroundProps(Props):
|
|
|
356
394
|
scale_type: Optional[ScaleType] = None
|
|
357
395
|
accessibility_label: Optional[str] = None
|
|
358
396
|
accessible: Optional[bool] = None
|
|
397
|
+
accessibility_state: Optional[Dict[str, Any]] = None
|
|
398
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None
|
|
399
|
+
test_id: Optional[str] = None
|
|
359
400
|
|
|
360
401
|
|
|
361
402
|
@dataclass(frozen=True)
|
|
@@ -370,6 +411,9 @@ class CheckboxProps(Props):
|
|
|
370
411
|
accessibility_label: Optional[str] = None
|
|
371
412
|
accessibility_hint: Optional[str] = None
|
|
372
413
|
accessible: Optional[bool] = None
|
|
414
|
+
accessibility_state: Optional[Dict[str, Any]] = None
|
|
415
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None
|
|
416
|
+
test_id: Optional[str] = None
|
|
373
417
|
|
|
374
418
|
|
|
375
419
|
@dataclass(frozen=True)
|
|
@@ -383,6 +427,9 @@ class SegmentedControlProps(Props):
|
|
|
383
427
|
tint_color: Optional[Color] = None
|
|
384
428
|
accessibility_label: Optional[str] = None
|
|
385
429
|
accessible: Optional[bool] = None
|
|
430
|
+
accessibility_state: Optional[Dict[str, Any]] = None
|
|
431
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None
|
|
432
|
+
test_id: Optional[str] = None
|
|
386
433
|
|
|
387
434
|
|
|
388
435
|
@dataclass(frozen=True)
|
|
@@ -403,6 +450,9 @@ class DatePickerProps(Props):
|
|
|
403
450
|
enabled: bool = True
|
|
404
451
|
accessibility_label: Optional[str] = None
|
|
405
452
|
accessible: Optional[bool] = None
|
|
453
|
+
accessibility_state: Optional[Dict[str, Any]] = None
|
|
454
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None
|
|
455
|
+
test_id: Optional[str] = None
|
|
406
456
|
|
|
407
457
|
|
|
408
458
|
# ======================================================================
|
|
@@ -410,18 +460,71 @@ class DatePickerProps(Props):
|
|
|
410
460
|
# ======================================================================
|
|
411
461
|
|
|
412
462
|
|
|
463
|
+
# Style keys that can vary per span inside a rich-text run. Layout
|
|
464
|
+
# keys are excluded: spans are inline fragments of one paragraph and
|
|
465
|
+
# have no boxes of their own.
|
|
466
|
+
_SPAN_STYLE_KEYS = (
|
|
467
|
+
"color",
|
|
468
|
+
"background_color",
|
|
469
|
+
"font_size",
|
|
470
|
+
"font_family",
|
|
471
|
+
"font_weight",
|
|
472
|
+
"bold",
|
|
473
|
+
"italic",
|
|
474
|
+
"text_decoration",
|
|
475
|
+
"letter_spacing",
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def _flatten_text_spans(
|
|
480
|
+
parts: Tuple[Any, ...],
|
|
481
|
+
inherited: Dict[str, Any],
|
|
482
|
+
) -> List[Dict[str, Any]]:
|
|
483
|
+
"""Flatten nested ``Text`` parts into a flat list of styled spans.
|
|
484
|
+
|
|
485
|
+
Strings become spans carrying only the inherited style overrides;
|
|
486
|
+
nested ``Text`` elements contribute their own span-style keys
|
|
487
|
+
(merged over what they inherit) and recurse into their parts.
|
|
488
|
+
"""
|
|
489
|
+
spans: List[Dict[str, Any]] = []
|
|
490
|
+
for part in parts:
|
|
491
|
+
if part is None:
|
|
492
|
+
continue
|
|
493
|
+
if isinstance(part, Element):
|
|
494
|
+
props = part.props or {}
|
|
495
|
+
child_style = dict(inherited)
|
|
496
|
+
for k in _SPAN_STYLE_KEYS:
|
|
497
|
+
if props.get(k) is not None:
|
|
498
|
+
child_style[k] = props[k]
|
|
499
|
+
nested = props.get("spans")
|
|
500
|
+
if nested:
|
|
501
|
+
for span in nested:
|
|
502
|
+
merged = dict(child_style)
|
|
503
|
+
for k in _SPAN_STYLE_KEYS:
|
|
504
|
+
if span.get(k) is not None:
|
|
505
|
+
merged[k] = span[k]
|
|
506
|
+
spans.append({"text": span.get("text", ""), **merged})
|
|
507
|
+
else:
|
|
508
|
+
spans.append({"text": str(props.get("text", "")), **child_style})
|
|
509
|
+
else:
|
|
510
|
+
spans.append({"text": str(part), **inherited})
|
|
511
|
+
return spans
|
|
512
|
+
|
|
513
|
+
|
|
413
514
|
def Text(
|
|
414
|
-
|
|
415
|
-
*,
|
|
515
|
+
*parts: Any,
|
|
416
516
|
style: StyleProp = None,
|
|
417
517
|
accessibility_label: Optional[str] = None,
|
|
418
518
|
accessibility_hint: Optional[str] = None,
|
|
419
519
|
accessibility_role: Optional[str] = None,
|
|
420
520
|
accessible: Optional[bool] = None,
|
|
521
|
+
accessibility_state: Optional[Dict[str, Any]] = None,
|
|
522
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None,
|
|
523
|
+
test_id: Optional[str] = None,
|
|
421
524
|
ref: Optional[Dict[str, Any]] = None,
|
|
422
525
|
key: Optional[str] = None,
|
|
423
526
|
) -> Element:
|
|
424
|
-
"""Display a string of text.
|
|
527
|
+
"""Display a string of text, optionally with styled nested spans.
|
|
425
528
|
|
|
426
529
|
Style properties: ``font_size``, ``color``, ``bold``,
|
|
427
530
|
``font_weight``, ``font_family``, ``italic``, ``text_align``,
|
|
@@ -431,29 +534,70 @@ def Text(
|
|
|
431
534
|
``border_color``, ``shadow_*``, ``opacity``, ``transform``, plus
|
|
432
535
|
the common layout props.
|
|
433
536
|
|
|
537
|
+
**Rich text**: pass multiple parts, mixing plain strings and
|
|
538
|
+
nested ``Text`` elements, to render one paragraph with per-span
|
|
539
|
+
styling (a single ``TextView`` / ``UILabel`` natively, so line
|
|
540
|
+
wrapping flows across spans):
|
|
541
|
+
|
|
542
|
+
```python
|
|
543
|
+
pn.Text(
|
|
544
|
+
"Hello, ",
|
|
545
|
+
pn.Text("world", style=pn.style(bold=True, color="#0A84FF")),
|
|
546
|
+
"!",
|
|
547
|
+
style=pn.style(font_size=18),
|
|
548
|
+
)
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
Nested spans inherit the outer element's text styling and may
|
|
552
|
+
override ``color``, ``background_color``, ``font_size``,
|
|
553
|
+
``font_family``, ``font_weight``, ``bold``, ``italic``,
|
|
554
|
+
``text_decoration``, and ``letter_spacing``.
|
|
555
|
+
|
|
434
556
|
Args:
|
|
435
|
-
|
|
557
|
+
*parts: Text content: a single string, or any mix of strings
|
|
558
|
+
and nested ``Text`` elements for rich text.
|
|
436
559
|
style: Style dict (or list of dicts).
|
|
437
560
|
accessibility_label: Spoken description for screen readers.
|
|
438
561
|
accessibility_hint: Spoken extra detail (iOS only).
|
|
439
562
|
accessibility_role: Semantic role for assistive tech.
|
|
440
563
|
accessible: Override whether the element is exposed to AT.
|
|
564
|
+
accessibility_state: Current widget state for assistive tech,
|
|
565
|
+
e.g. ``{"disabled": True, "selected": False}``. Recognized
|
|
566
|
+
keys: ``disabled``, ``selected``, ``checked``, ``busy``,
|
|
567
|
+
``expanded``.
|
|
568
|
+
accessibility_live_region: How AT announces dynamic changes to
|
|
569
|
+
this view: ``"none"``, ``"polite"``, or ``"assertive"``
|
|
570
|
+
(Android only).
|
|
571
|
+
test_id: Stable identifier for UI tests; exposed as
|
|
572
|
+
``resource-id`` on Android and ``accessibilityIdentifier``
|
|
573
|
+
on iOS.
|
|
441
574
|
ref: Optional ``use_ref()`` dict.
|
|
442
575
|
key: Stable identity for keyed reconciliation.
|
|
443
576
|
|
|
444
577
|
Returns:
|
|
445
578
|
An [`Element`][pythonnative.Element] of type ``"Text"``.
|
|
446
579
|
"""
|
|
580
|
+
rich = any(isinstance(p, Element) for p in parts) or len(parts) > 1
|
|
581
|
+
if rich:
|
|
582
|
+
spans = _flatten_text_spans(parts, {})
|
|
583
|
+
text = "".join(s["text"] for s in spans)
|
|
584
|
+
else:
|
|
585
|
+
spans = None
|
|
586
|
+
text = str(parts[0]) if parts else ""
|
|
447
587
|
return _make_element(
|
|
448
588
|
"Text",
|
|
449
589
|
style=style,
|
|
450
590
|
ref=ref,
|
|
451
591
|
key=key,
|
|
452
592
|
text=text,
|
|
593
|
+
spans=spans,
|
|
453
594
|
accessibility_label=accessibility_label,
|
|
454
595
|
accessibility_hint=accessibility_hint,
|
|
455
596
|
accessibility_role=accessibility_role,
|
|
456
597
|
accessible=accessible,
|
|
598
|
+
accessibility_state=accessibility_state,
|
|
599
|
+
accessibility_live_region=accessibility_live_region,
|
|
600
|
+
test_id=test_id,
|
|
457
601
|
)
|
|
458
602
|
|
|
459
603
|
|
|
@@ -467,6 +611,9 @@ def Button(
|
|
|
467
611
|
accessibility_hint: Optional[str] = None,
|
|
468
612
|
accessibility_role: Optional[str] = None,
|
|
469
613
|
accessible: Optional[bool] = None,
|
|
614
|
+
accessibility_state: Optional[Dict[str, Any]] = None,
|
|
615
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None,
|
|
616
|
+
test_id: Optional[str] = None,
|
|
470
617
|
ref: Optional[Dict[str, Any]] = None,
|
|
471
618
|
key: Optional[str] = None,
|
|
472
619
|
) -> Element:
|
|
@@ -488,6 +635,16 @@ def Button(
|
|
|
488
635
|
accessibility_hint: Spoken extra detail (iOS only).
|
|
489
636
|
accessibility_role: Override the default ``"button"`` role.
|
|
490
637
|
accessible: Override whether the element is exposed to AT.
|
|
638
|
+
accessibility_state: Current widget state for assistive tech,
|
|
639
|
+
e.g. ``{"disabled": True, "selected": False}``. Recognized
|
|
640
|
+
keys: ``disabled``, ``selected``, ``checked``, ``busy``,
|
|
641
|
+
``expanded``.
|
|
642
|
+
accessibility_live_region: How AT announces dynamic changes to
|
|
643
|
+
this view: ``"none"``, ``"polite"``, or ``"assertive"``
|
|
644
|
+
(Android only).
|
|
645
|
+
test_id: Stable identifier for UI tests; exposed as
|
|
646
|
+
``resource-id`` on Android and ``accessibilityIdentifier``
|
|
647
|
+
on iOS.
|
|
491
648
|
ref: Optional ``use_ref()`` dict.
|
|
492
649
|
key: Stable identity for keyed reconciliation.
|
|
493
650
|
|
|
@@ -506,6 +663,9 @@ def Button(
|
|
|
506
663
|
accessibility_hint=accessibility_hint,
|
|
507
664
|
accessibility_role=accessibility_role,
|
|
508
665
|
accessible=accessible,
|
|
666
|
+
accessibility_state=accessibility_state,
|
|
667
|
+
accessibility_live_region=accessibility_live_region,
|
|
668
|
+
test_id=test_id,
|
|
509
669
|
_defaults={"accessibility_role": "button"},
|
|
510
670
|
)
|
|
511
671
|
|
|
@@ -535,6 +695,9 @@ def TextInput(
|
|
|
535
695
|
accessibility_label: Optional[str] = None,
|
|
536
696
|
accessibility_hint: Optional[str] = None,
|
|
537
697
|
accessible: Optional[bool] = None,
|
|
698
|
+
accessibility_state: Optional[Dict[str, Any]] = None,
|
|
699
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None,
|
|
700
|
+
test_id: Optional[str] = None,
|
|
538
701
|
ref: Optional[Dict[str, Any]] = None,
|
|
539
702
|
key: Optional[str] = None,
|
|
540
703
|
) -> Element:
|
|
@@ -575,6 +738,16 @@ def TextInput(
|
|
|
575
738
|
accessibility_label: Spoken description for screen readers.
|
|
576
739
|
accessibility_hint: Spoken extra detail (iOS only).
|
|
577
740
|
accessible: Override whether the element is exposed to AT.
|
|
741
|
+
accessibility_state: Current widget state for assistive tech,
|
|
742
|
+
e.g. ``{"disabled": True, "selected": False}``. Recognized
|
|
743
|
+
keys: ``disabled``, ``selected``, ``checked``, ``busy``,
|
|
744
|
+
``expanded``.
|
|
745
|
+
accessibility_live_region: How AT announces dynamic changes to
|
|
746
|
+
this view: ``"none"``, ``"polite"``, or ``"assertive"``
|
|
747
|
+
(Android only).
|
|
748
|
+
test_id: Stable identifier for UI tests; exposed as
|
|
749
|
+
``resource-id`` on Android and ``accessibilityIdentifier``
|
|
750
|
+
on iOS.
|
|
578
751
|
ref: Optional ``use_ref()`` dict.
|
|
579
752
|
key: Stable identity for keyed reconciliation.
|
|
580
753
|
|
|
@@ -608,6 +781,9 @@ def TextInput(
|
|
|
608
781
|
accessibility_label=accessibility_label,
|
|
609
782
|
accessibility_hint=accessibility_hint,
|
|
610
783
|
accessible=accessible,
|
|
784
|
+
accessibility_state=accessibility_state,
|
|
785
|
+
accessibility_live_region=accessibility_live_region,
|
|
786
|
+
test_id=test_id,
|
|
611
787
|
)
|
|
612
788
|
|
|
613
789
|
|
|
@@ -616,10 +792,16 @@ def Image(
|
|
|
616
792
|
*,
|
|
617
793
|
scale_type: Optional[ScaleType] = None,
|
|
618
794
|
tint_color: Optional[Color] = None,
|
|
795
|
+
placeholder_color: Optional[Color] = None,
|
|
796
|
+
on_load: Optional[Callable[[], None]] = None,
|
|
797
|
+
on_error: Optional[Callable[[str], None]] = None,
|
|
619
798
|
style: StyleProp = None,
|
|
620
799
|
accessibility_label: Optional[str] = None,
|
|
621
800
|
accessibility_role: Optional[str] = None,
|
|
622
801
|
accessible: Optional[bool] = None,
|
|
802
|
+
accessibility_state: Optional[Dict[str, Any]] = None,
|
|
803
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None,
|
|
804
|
+
test_id: Optional[str] = None,
|
|
623
805
|
ref: Optional[Dict[str, Any]] = None,
|
|
624
806
|
key: Optional[str] = None,
|
|
625
807
|
) -> Element:
|
|
@@ -628,10 +810,11 @@ def Image(
|
|
|
628
810
|
Style properties: ``background_color``, ``border_*``, ``opacity``,
|
|
629
811
|
``transform``, plus the common layout props.
|
|
630
812
|
|
|
631
|
-
Network images (``http://`` / ``https://``)
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
813
|
+
Network images (``http://`` / ``https://``) go through the shared
|
|
814
|
+
image pipeline (`pythonnative.images`): downloads happen on a
|
|
815
|
+
background thread, bytes are cached in memory and on disk keyed by
|
|
816
|
+
URL, concurrent requests for the same URL share one download, and
|
|
817
|
+
large bitmaps are downsampled to the view size when decoded.
|
|
635
818
|
|
|
636
819
|
Args:
|
|
637
820
|
source: Image resource name or URL.
|
|
@@ -639,10 +822,26 @@ def Image(
|
|
|
639
822
|
``"center"``.
|
|
640
823
|
tint_color: Color overlay applied to template images
|
|
641
824
|
(monochrome icons).
|
|
825
|
+
placeholder_color: Background color shown while a remote image
|
|
826
|
+
is loading (and left in place if it fails).
|
|
827
|
+
on_load: Callback invoked once the image has been decoded and
|
|
828
|
+
displayed.
|
|
829
|
+
on_error: Callback invoked with an error message when a remote
|
|
830
|
+
image fails to download or decode.
|
|
642
831
|
style: Style dict (or list of dicts).
|
|
643
832
|
accessibility_label: Spoken description for screen readers.
|
|
644
833
|
accessibility_role: Override the default ``"image"`` role.
|
|
645
834
|
accessible: Override whether the element is exposed to AT.
|
|
835
|
+
accessibility_state: Current widget state for assistive tech,
|
|
836
|
+
e.g. ``{"disabled": True, "selected": False}``. Recognized
|
|
837
|
+
keys: ``disabled``, ``selected``, ``checked``, ``busy``,
|
|
838
|
+
``expanded``.
|
|
839
|
+
accessibility_live_region: How AT announces dynamic changes to
|
|
840
|
+
this view: ``"none"``, ``"polite"``, or ``"assertive"``
|
|
841
|
+
(Android only).
|
|
842
|
+
test_id: Stable identifier for UI tests; exposed as
|
|
843
|
+
``resource-id`` on Android and ``accessibilityIdentifier``
|
|
844
|
+
on iOS.
|
|
646
845
|
ref: Optional ``use_ref()`` dict.
|
|
647
846
|
key: Stable identity for keyed reconciliation.
|
|
648
847
|
|
|
@@ -657,9 +856,15 @@ def Image(
|
|
|
657
856
|
source=source or None,
|
|
658
857
|
scale_type=scale_type,
|
|
659
858
|
tint_color=tint_color,
|
|
859
|
+
placeholder_color=placeholder_color,
|
|
860
|
+
on_load=on_load,
|
|
861
|
+
on_error=on_error,
|
|
660
862
|
accessibility_label=accessibility_label,
|
|
661
863
|
accessibility_role=accessibility_role,
|
|
662
864
|
accessible=accessible,
|
|
865
|
+
accessibility_state=accessibility_state,
|
|
866
|
+
accessibility_live_region=accessibility_live_region,
|
|
867
|
+
test_id=test_id,
|
|
663
868
|
_defaults={"accessibility_role": "image"},
|
|
664
869
|
)
|
|
665
870
|
|
|
@@ -900,6 +1105,9 @@ def View(
|
|
|
900
1105
|
accessibility_hint: Optional[str] = None,
|
|
901
1106
|
accessibility_role: Optional[str] = None,
|
|
902
1107
|
accessible: Optional[bool] = None,
|
|
1108
|
+
accessibility_state: Optional[Dict[str, Any]] = None,
|
|
1109
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None,
|
|
1110
|
+
test_id: Optional[str] = None,
|
|
903
1111
|
ref: Optional[Dict[str, Any]] = None,
|
|
904
1112
|
key: Optional[str] = None,
|
|
905
1113
|
) -> Element:
|
|
@@ -939,6 +1147,16 @@ def View(
|
|
|
939
1147
|
accessibility_hint: Spoken extra detail (iOS only).
|
|
940
1148
|
accessibility_role: Semantic role for assistive tech.
|
|
941
1149
|
accessible: Override whether the element is exposed to AT.
|
|
1150
|
+
accessibility_state: Current widget state for assistive tech,
|
|
1151
|
+
e.g. ``{"disabled": True, "selected": False}``. Recognized
|
|
1152
|
+
keys: ``disabled``, ``selected``, ``checked``, ``busy``,
|
|
1153
|
+
``expanded``.
|
|
1154
|
+
accessibility_live_region: How AT announces dynamic changes to
|
|
1155
|
+
this view: ``"none"``, ``"polite"``, or ``"assertive"``
|
|
1156
|
+
(Android only).
|
|
1157
|
+
test_id: Stable identifier for UI tests; exposed as
|
|
1158
|
+
``resource-id`` on Android and ``accessibilityIdentifier``
|
|
1159
|
+
on iOS.
|
|
942
1160
|
ref: Optional ``use_ref()`` dict.
|
|
943
1161
|
key: Stable identity for keyed reconciliation.
|
|
944
1162
|
|
|
@@ -956,6 +1174,9 @@ def View(
|
|
|
956
1174
|
accessibility_hint=accessibility_hint,
|
|
957
1175
|
accessibility_role=accessibility_role,
|
|
958
1176
|
accessible=accessible,
|
|
1177
|
+
accessibility_state=accessibility_state,
|
|
1178
|
+
accessibility_live_region=accessibility_live_region,
|
|
1179
|
+
test_id=test_id,
|
|
959
1180
|
_defaults={"flex_direction": "column"},
|
|
960
1181
|
)
|
|
961
1182
|
|
|
@@ -1087,25 +1308,76 @@ def ScrollView(
|
|
|
1087
1308
|
)
|
|
1088
1309
|
|
|
1089
1310
|
|
|
1311
|
+
_SAFE_AREA_EDGES: Tuple[str, ...] = ("top", "left", "bottom", "right")
|
|
1312
|
+
|
|
1313
|
+
|
|
1314
|
+
def _numeric_edge_padding(style: Dict[str, Any], edge: str) -> float:
|
|
1315
|
+
"""Return the numeric padding already declared for ``edge`` in ``style``.
|
|
1316
|
+
|
|
1317
|
+
Only numeric values participate; percentage strings and dict
|
|
1318
|
+
shorthands are left alone (the inset simply overrides them for
|
|
1319
|
+
that edge). Resolution order matches the layout engine:
|
|
1320
|
+
``padding_{edge}`` beats the axis shorthand, which beats
|
|
1321
|
+
``padding``.
|
|
1322
|
+
"""
|
|
1323
|
+
axis_key = "padding_vertical" if edge in ("top", "bottom") else "padding_horizontal"
|
|
1324
|
+
for key in (f"padding_{edge}", axis_key, "padding"):
|
|
1325
|
+
value = style.get(key)
|
|
1326
|
+
if isinstance(value, (int, float)) and not isinstance(value, bool):
|
|
1327
|
+
return float(value)
|
|
1328
|
+
return 0.0
|
|
1329
|
+
|
|
1330
|
+
|
|
1331
|
+
@component
|
|
1332
|
+
def _SafeAreaContainer(**p: Any) -> Element:
|
|
1333
|
+
"""Hook-driven body of `SafeAreaView`.
|
|
1334
|
+
|
|
1335
|
+
Reads the live insets via
|
|
1336
|
+
[`use_safe_area_insets`][pythonnative.use_safe_area_insets] (so the
|
|
1337
|
+
subtree re-renders when the platform publishes new values, e.g. on
|
|
1338
|
+
rotation) and adds each selected edge's inset on top of any padding
|
|
1339
|
+
the user declared for that edge.
|
|
1340
|
+
"""
|
|
1341
|
+
insets = use_safe_area_insets()
|
|
1342
|
+
style: Dict[str, Any] = dict(p.get("style") or {})
|
|
1343
|
+
edges = p.get("edges") or _SAFE_AREA_EDGES
|
|
1344
|
+
for edge in _SAFE_AREA_EDGES:
|
|
1345
|
+
if edge not in edges:
|
|
1346
|
+
continue
|
|
1347
|
+
inset = float(insets.get(edge, 0.0) or 0.0)
|
|
1348
|
+
if inset > 0:
|
|
1349
|
+
style[f"padding_{edge}"] = _numeric_edge_padding(style, edge) + inset
|
|
1350
|
+
children = p.get("children") or []
|
|
1351
|
+
return Element("SafeAreaView", style, list(children))
|
|
1352
|
+
|
|
1353
|
+
|
|
1090
1354
|
def SafeAreaView(
|
|
1091
1355
|
*children: Element,
|
|
1356
|
+
edges: Optional[Tuple[Literal["top", "left", "bottom", "right"], ...]] = None,
|
|
1092
1357
|
style: StyleProp = None,
|
|
1093
1358
|
key: Optional[str] = None,
|
|
1094
1359
|
) -> Element:
|
|
1095
1360
|
"""Container that respects safe-area insets (notch, status bar, home indicator).
|
|
1096
1361
|
|
|
1362
|
+
Applies the platform-reported insets as extra padding on the
|
|
1363
|
+
selected edges and re-renders automatically when the insets change
|
|
1364
|
+
(rotation, split view). User padding on an inset edge is added to
|
|
1365
|
+
the inset, matching ``react-native-safe-area-context``.
|
|
1366
|
+
|
|
1097
1367
|
Args:
|
|
1098
1368
|
*children: Child elements that should avoid system UI overlays.
|
|
1369
|
+
edges: Which edges to pad; defaults to all four.
|
|
1099
1370
|
style: Style dict (or list of dicts).
|
|
1100
1371
|
key: Stable identity for keyed reconciliation.
|
|
1101
1372
|
|
|
1102
1373
|
Returns:
|
|
1103
|
-
An [`Element`][pythonnative.Element]
|
|
1374
|
+
An [`Element`][pythonnative.Element] that renders a
|
|
1375
|
+
``"SafeAreaView"`` container.
|
|
1104
1376
|
"""
|
|
1105
|
-
return
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
style=style,
|
|
1377
|
+
return _SafeAreaContainer(
|
|
1378
|
+
children=list(children),
|
|
1379
|
+
edges=tuple(edges) if edges else None,
|
|
1380
|
+
style=resolve_style(style),
|
|
1109
1381
|
key=key,
|
|
1110
1382
|
)
|
|
1111
1383
|
|
|
@@ -1175,6 +1447,41 @@ def Modal(
|
|
|
1175
1447
|
)
|
|
1176
1448
|
|
|
1177
1449
|
|
|
1450
|
+
@component
|
|
1451
|
+
def _StatefulPressable(**p: Any) -> Element:
|
|
1452
|
+
"""Hook-driven Pressable used when ``style`` is a callable.
|
|
1453
|
+
|
|
1454
|
+
Tracks the pressed state with ``use_state`` and re-invokes the
|
|
1455
|
+
user's style function with ``{"pressed": bool}`` on every press
|
|
1456
|
+
transition, mirroring React Native's function-style ``style`` prop.
|
|
1457
|
+
"""
|
|
1458
|
+
pressed, set_pressed = use_state(False)
|
|
1459
|
+
style_fn = p["style_fn"]
|
|
1460
|
+
user_press_in = p.get("on_press_in")
|
|
1461
|
+
user_press_out = p.get("on_press_out")
|
|
1462
|
+
|
|
1463
|
+
def _press_in() -> None:
|
|
1464
|
+
set_pressed(True)
|
|
1465
|
+
if user_press_in is not None:
|
|
1466
|
+
user_press_in()
|
|
1467
|
+
|
|
1468
|
+
def _press_out() -> None:
|
|
1469
|
+
set_pressed(False)
|
|
1470
|
+
if user_press_out is not None:
|
|
1471
|
+
user_press_out()
|
|
1472
|
+
|
|
1473
|
+
forwarded = {k: v for k, v in p.items() if k not in ("children", "style_fn", "on_press_in", "on_press_out")}
|
|
1474
|
+
return _make_element(
|
|
1475
|
+
"Pressable",
|
|
1476
|
+
*(p.get("children") or []),
|
|
1477
|
+
style=resolve_style(style_fn({"pressed": pressed})),
|
|
1478
|
+
on_press_in=_press_in,
|
|
1479
|
+
on_press_out=_press_out,
|
|
1480
|
+
_defaults={"accessibility_role": "button"},
|
|
1481
|
+
**forwarded,
|
|
1482
|
+
)
|
|
1483
|
+
|
|
1484
|
+
|
|
1178
1485
|
def Pressable(
|
|
1179
1486
|
*children: Element,
|
|
1180
1487
|
on_press: Optional[Callable[[], None]] = None,
|
|
@@ -1183,11 +1490,14 @@ def Pressable(
|
|
|
1183
1490
|
on_press_out: Optional[Callable[[], None]] = None,
|
|
1184
1491
|
pressed_opacity: float = 0.6,
|
|
1185
1492
|
gestures: Optional[List[Any]] = None,
|
|
1186
|
-
style: StyleProp = None,
|
|
1493
|
+
style: Union[StyleProp, Callable[[Dict[str, bool]], StyleProp]] = None,
|
|
1187
1494
|
accessibility_label: Optional[str] = None,
|
|
1188
1495
|
accessibility_hint: Optional[str] = None,
|
|
1189
1496
|
accessibility_role: Optional[str] = None,
|
|
1190
1497
|
accessible: Optional[bool] = None,
|
|
1498
|
+
accessibility_state: Optional[Dict[str, Any]] = None,
|
|
1499
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None,
|
|
1500
|
+
test_id: Optional[str] = None,
|
|
1191
1501
|
ref: Optional[Dict[str, Any]] = None,
|
|
1192
1502
|
key: Optional[str] = None,
|
|
1193
1503
|
) -> Element:
|
|
@@ -1210,17 +1520,60 @@ def Pressable(
|
|
|
1210
1520
|
gestures: Optional list of gesture descriptors from
|
|
1211
1521
|
`pythonnative.gestures` recognized natively on this view
|
|
1212
1522
|
(pan / swipe / pinch / rotation / multi-tap).
|
|
1213
|
-
style: Style dict applied to the wrapper
|
|
1523
|
+
style: Style dict applied to the wrapper, or a callable
|
|
1524
|
+
receiving the interaction state (``{"pressed": bool}``)
|
|
1525
|
+
and returning a style, re-evaluated on every press
|
|
1526
|
+
transition:
|
|
1527
|
+
|
|
1528
|
+
```python
|
|
1529
|
+
pn.Pressable(
|
|
1530
|
+
pn.Text("Tap"),
|
|
1531
|
+
style=lambda s: pn.style(
|
|
1532
|
+
background_color="#0051A8" if s["pressed"] else "#007AFF",
|
|
1533
|
+
),
|
|
1534
|
+
)
|
|
1535
|
+
```
|
|
1214
1536
|
accessibility_label: Spoken description for screen readers.
|
|
1215
1537
|
accessibility_hint: Spoken extra detail (iOS only).
|
|
1216
1538
|
accessibility_role: Override the default ``"button"`` role.
|
|
1217
1539
|
accessible: Override whether the element is exposed to AT.
|
|
1540
|
+
accessibility_state: Current widget state for assistive tech,
|
|
1541
|
+
e.g. ``{"disabled": True, "selected": False}``. Recognized
|
|
1542
|
+
keys: ``disabled``, ``selected``, ``checked``, ``busy``,
|
|
1543
|
+
``expanded``.
|
|
1544
|
+
accessibility_live_region: How AT announces dynamic changes to
|
|
1545
|
+
this view: ``"none"``, ``"polite"``, or ``"assertive"``
|
|
1546
|
+
(Android only).
|
|
1547
|
+
test_id: Stable identifier for UI tests; exposed as
|
|
1548
|
+
``resource-id`` on Android and ``accessibilityIdentifier``
|
|
1549
|
+
on iOS.
|
|
1218
1550
|
ref: Optional ``use_ref()`` dict.
|
|
1219
1551
|
key: Stable identity for keyed reconciliation.
|
|
1220
1552
|
|
|
1221
1553
|
Returns:
|
|
1222
|
-
An [`Element`][pythonnative.Element] of type ``"Pressable"
|
|
1554
|
+
An [`Element`][pythonnative.Element] of type ``"Pressable"``
|
|
1555
|
+
(wrapped in a stateful composite when ``style`` is callable).
|
|
1223
1556
|
"""
|
|
1557
|
+
if callable(style):
|
|
1558
|
+
return _StatefulPressable(
|
|
1559
|
+
children=list(children),
|
|
1560
|
+
style_fn=style,
|
|
1561
|
+
ref=ref,
|
|
1562
|
+
key=key,
|
|
1563
|
+
on_press=on_press,
|
|
1564
|
+
on_long_press=on_long_press,
|
|
1565
|
+
on_press_in=on_press_in,
|
|
1566
|
+
on_press_out=on_press_out,
|
|
1567
|
+
pressed_opacity=pressed_opacity,
|
|
1568
|
+
gestures=gestures,
|
|
1569
|
+
accessibility_label=accessibility_label,
|
|
1570
|
+
accessibility_hint=accessibility_hint,
|
|
1571
|
+
accessibility_role=accessibility_role,
|
|
1572
|
+
accessible=accessible,
|
|
1573
|
+
accessibility_state=accessibility_state,
|
|
1574
|
+
accessibility_live_region=accessibility_live_region,
|
|
1575
|
+
test_id=test_id,
|
|
1576
|
+
)
|
|
1224
1577
|
return _make_element(
|
|
1225
1578
|
"Pressable",
|
|
1226
1579
|
*children,
|
|
@@ -1237,6 +1590,9 @@ def Pressable(
|
|
|
1237
1590
|
accessibility_hint=accessibility_hint,
|
|
1238
1591
|
accessibility_role=accessibility_role,
|
|
1239
1592
|
accessible=accessible,
|
|
1593
|
+
accessibility_state=accessibility_state,
|
|
1594
|
+
accessibility_live_region=accessibility_live_region,
|
|
1595
|
+
test_id=test_id,
|
|
1240
1596
|
_defaults={"accessibility_role": "button"},
|
|
1241
1597
|
)
|
|
1242
1598
|
|
|
@@ -1257,6 +1613,9 @@ def TouchableOpacity(
|
|
|
1257
1613
|
accessibility_hint: Optional[str] = None,
|
|
1258
1614
|
accessibility_role: Optional[str] = None,
|
|
1259
1615
|
accessible: Optional[bool] = None,
|
|
1616
|
+
accessibility_state: Optional[Dict[str, Any]] = None,
|
|
1617
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None,
|
|
1618
|
+
test_id: Optional[str] = None,
|
|
1260
1619
|
key: Optional[str] = None,
|
|
1261
1620
|
) -> Element:
|
|
1262
1621
|
"""Wrap children so they fade to ``active_opacity`` while pressed.
|
|
@@ -1278,6 +1637,16 @@ def TouchableOpacity(
|
|
|
1278
1637
|
accessibility_hint: Spoken extra detail (iOS only).
|
|
1279
1638
|
accessibility_role: Override the default ``"button"`` role.
|
|
1280
1639
|
accessible: Override whether the element is exposed to AT.
|
|
1640
|
+
accessibility_state: Current widget state for assistive tech,
|
|
1641
|
+
e.g. ``{"disabled": True, "selected": False}``. Recognized
|
|
1642
|
+
keys: ``disabled``, ``selected``, ``checked``, ``busy``,
|
|
1643
|
+
``expanded``.
|
|
1644
|
+
accessibility_live_region: How AT announces dynamic changes to
|
|
1645
|
+
this view: ``"none"``, ``"polite"``, or ``"assertive"``
|
|
1646
|
+
(Android only).
|
|
1647
|
+
test_id: Stable identifier for UI tests; exposed as
|
|
1648
|
+
``resource-id`` on Android and ``accessibilityIdentifier``
|
|
1649
|
+
on iOS.
|
|
1281
1650
|
key: Stable identity for keyed reconciliation.
|
|
1282
1651
|
|
|
1283
1652
|
Returns:
|
|
@@ -1300,6 +1669,9 @@ def TouchableOpacity(
|
|
|
1300
1669
|
accessibility_hint=accessibility_hint,
|
|
1301
1670
|
accessibility_role=accessibility_role,
|
|
1302
1671
|
accessible=accessible,
|
|
1672
|
+
accessibility_state=accessibility_state,
|
|
1673
|
+
accessibility_live_region=accessibility_live_region,
|
|
1674
|
+
test_id=test_id,
|
|
1303
1675
|
key=key,
|
|
1304
1676
|
)
|
|
1305
1677
|
|
|
@@ -1311,6 +1683,9 @@ def ImageBackground(
|
|
|
1311
1683
|
style: StyleProp = None,
|
|
1312
1684
|
accessibility_label: Optional[str] = None,
|
|
1313
1685
|
accessible: Optional[bool] = None,
|
|
1686
|
+
accessibility_state: Optional[Dict[str, Any]] = None,
|
|
1687
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None,
|
|
1688
|
+
test_id: Optional[str] = None,
|
|
1314
1689
|
key: Optional[str] = None,
|
|
1315
1690
|
) -> Element:
|
|
1316
1691
|
"""Render ``children`` layered on top of a background image.
|
|
@@ -1329,6 +1704,16 @@ def ImageBackground(
|
|
|
1329
1704
|
style: Style dict for the container (size, padding, alignment).
|
|
1330
1705
|
accessibility_label: Spoken description of the background image.
|
|
1331
1706
|
accessible: Override whether the image is exposed to AT.
|
|
1707
|
+
accessibility_state: Current widget state for assistive tech,
|
|
1708
|
+
e.g. ``{"disabled": True, "selected": False}``. Recognized
|
|
1709
|
+
keys: ``disabled``, ``selected``, ``checked``, ``busy``,
|
|
1710
|
+
``expanded``.
|
|
1711
|
+
accessibility_live_region: How AT announces dynamic changes to
|
|
1712
|
+
this view: ``"none"``, ``"polite"``, or ``"assertive"``
|
|
1713
|
+
(Android only).
|
|
1714
|
+
test_id: Stable identifier for UI tests; exposed as
|
|
1715
|
+
``resource-id`` on Android and ``accessibilityIdentifier``
|
|
1716
|
+
on iOS.
|
|
1332
1717
|
key: Stable identity for keyed reconciliation.
|
|
1333
1718
|
|
|
1334
1719
|
Returns:
|
|
@@ -1342,6 +1727,9 @@ def ImageBackground(
|
|
|
1342
1727
|
style=fill,
|
|
1343
1728
|
accessibility_label=accessibility_label,
|
|
1344
1729
|
accessible=accessible,
|
|
1730
|
+
accessibility_state=accessibility_state,
|
|
1731
|
+
accessibility_live_region=accessibility_live_region,
|
|
1732
|
+
test_id=test_id,
|
|
1345
1733
|
)
|
|
1346
1734
|
content = View(*children, style={"flex": 1})
|
|
1347
1735
|
return View(
|
|
@@ -1363,6 +1751,9 @@ def Checkbox(
|
|
|
1363
1751
|
accessibility_label: Optional[str] = None,
|
|
1364
1752
|
accessibility_hint: Optional[str] = None,
|
|
1365
1753
|
accessible: Optional[bool] = None,
|
|
1754
|
+
accessibility_state: Optional[Dict[str, Any]] = None,
|
|
1755
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None,
|
|
1756
|
+
test_id: Optional[str] = None,
|
|
1366
1757
|
key: Optional[str] = None,
|
|
1367
1758
|
) -> Element:
|
|
1368
1759
|
"""A boolean checkbox with an optional inline label.
|
|
@@ -1381,6 +1772,16 @@ def Checkbox(
|
|
|
1381
1772
|
accessibility_label: Spoken description for screen readers.
|
|
1382
1773
|
accessibility_hint: Spoken extra detail (iOS only).
|
|
1383
1774
|
accessible: Override whether the element is exposed to AT.
|
|
1775
|
+
accessibility_state: Current widget state for assistive tech,
|
|
1776
|
+
e.g. ``{"disabled": True, "selected": False}``. Recognized
|
|
1777
|
+
keys: ``disabled``, ``selected``, ``checked``, ``busy``,
|
|
1778
|
+
``expanded``.
|
|
1779
|
+
accessibility_live_region: How AT announces dynamic changes to
|
|
1780
|
+
this view: ``"none"``, ``"polite"``, or ``"assertive"``
|
|
1781
|
+
(Android only).
|
|
1782
|
+
test_id: Stable identifier for UI tests; exposed as
|
|
1783
|
+
``resource-id`` on Android and ``accessibilityIdentifier``
|
|
1784
|
+
on iOS.
|
|
1384
1785
|
key: Stable identity for keyed reconciliation.
|
|
1385
1786
|
|
|
1386
1787
|
Returns:
|
|
@@ -1398,6 +1799,9 @@ def Checkbox(
|
|
|
1398
1799
|
accessibility_label=accessibility_label,
|
|
1399
1800
|
accessibility_hint=accessibility_hint,
|
|
1400
1801
|
accessible=accessible,
|
|
1802
|
+
accessibility_state=accessibility_state,
|
|
1803
|
+
accessibility_live_region=accessibility_live_region,
|
|
1804
|
+
test_id=test_id,
|
|
1401
1805
|
_defaults={"accessibility_role": "checkbox"},
|
|
1402
1806
|
)
|
|
1403
1807
|
|
|
@@ -1412,6 +1816,9 @@ def SegmentedControl(
|
|
|
1412
1816
|
style: StyleProp = None,
|
|
1413
1817
|
accessibility_label: Optional[str] = None,
|
|
1414
1818
|
accessible: Optional[bool] = None,
|
|
1819
|
+
accessibility_state: Optional[Dict[str, Any]] = None,
|
|
1820
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None,
|
|
1821
|
+
test_id: Optional[str] = None,
|
|
1415
1822
|
key: Optional[str] = None,
|
|
1416
1823
|
) -> Element:
|
|
1417
1824
|
"""A horizontal multi-choice control (one selected segment at a time).
|
|
@@ -1428,6 +1835,16 @@ def SegmentedControl(
|
|
|
1428
1835
|
style: Style dict (or list of dicts).
|
|
1429
1836
|
accessibility_label: Spoken description for screen readers.
|
|
1430
1837
|
accessible: Override whether the element is exposed to AT.
|
|
1838
|
+
accessibility_state: Current widget state for assistive tech,
|
|
1839
|
+
e.g. ``{"disabled": True, "selected": False}``. Recognized
|
|
1840
|
+
keys: ``disabled``, ``selected``, ``checked``, ``busy``,
|
|
1841
|
+
``expanded``.
|
|
1842
|
+
accessibility_live_region: How AT announces dynamic changes to
|
|
1843
|
+
this view: ``"none"``, ``"polite"``, or ``"assertive"``
|
|
1844
|
+
(Android only).
|
|
1845
|
+
test_id: Stable identifier for UI tests; exposed as
|
|
1846
|
+
``resource-id`` on Android and ``accessibilityIdentifier``
|
|
1847
|
+
on iOS.
|
|
1431
1848
|
key: Stable identity for keyed reconciliation.
|
|
1432
1849
|
|
|
1433
1850
|
Returns:
|
|
@@ -1445,6 +1862,9 @@ def SegmentedControl(
|
|
|
1445
1862
|
tint_color=tint_color,
|
|
1446
1863
|
accessibility_label=accessibility_label,
|
|
1447
1864
|
accessible=accessible,
|
|
1865
|
+
accessibility_state=accessibility_state,
|
|
1866
|
+
accessibility_live_region=accessibility_live_region,
|
|
1867
|
+
test_id=test_id,
|
|
1448
1868
|
)
|
|
1449
1869
|
|
|
1450
1870
|
|
|
@@ -1459,6 +1879,9 @@ def DatePicker(
|
|
|
1459
1879
|
style: StyleProp = None,
|
|
1460
1880
|
accessibility_label: Optional[str] = None,
|
|
1461
1881
|
accessible: Optional[bool] = None,
|
|
1882
|
+
accessibility_state: Optional[Dict[str, Any]] = None,
|
|
1883
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None,
|
|
1884
|
+
test_id: Optional[str] = None,
|
|
1462
1885
|
key: Optional[str] = None,
|
|
1463
1886
|
) -> Element:
|
|
1464
1887
|
"""A native date / time picker.
|
|
@@ -1478,6 +1901,16 @@ def DatePicker(
|
|
|
1478
1901
|
style: Style dict (or list of dicts).
|
|
1479
1902
|
accessibility_label: Spoken description for screen readers.
|
|
1480
1903
|
accessible: Override whether the element is exposed to AT.
|
|
1904
|
+
accessibility_state: Current widget state for assistive tech,
|
|
1905
|
+
e.g. ``{"disabled": True, "selected": False}``. Recognized
|
|
1906
|
+
keys: ``disabled``, ``selected``, ``checked``, ``busy``,
|
|
1907
|
+
``expanded``.
|
|
1908
|
+
accessibility_live_region: How AT announces dynamic changes to
|
|
1909
|
+
this view: ``"none"``, ``"polite"``, or ``"assertive"``
|
|
1910
|
+
(Android only).
|
|
1911
|
+
test_id: Stable identifier for UI tests; exposed as
|
|
1912
|
+
``resource-id`` on Android and ``accessibilityIdentifier``
|
|
1913
|
+
on iOS.
|
|
1481
1914
|
key: Stable identity for keyed reconciliation.
|
|
1482
1915
|
|
|
1483
1916
|
Returns:
|
|
@@ -1495,6 +1928,9 @@ def DatePicker(
|
|
|
1495
1928
|
enabled=False if enabled is False else None,
|
|
1496
1929
|
accessibility_label=accessibility_label,
|
|
1497
1930
|
accessible=accessible,
|
|
1931
|
+
accessibility_state=accessibility_state,
|
|
1932
|
+
accessibility_live_region=accessibility_live_region,
|
|
1933
|
+
test_id=test_id,
|
|
1498
1934
|
_defaults={"accessibility_role": "button"},
|
|
1499
1935
|
)
|
|
1500
1936
|
|
|
@@ -1600,17 +2036,26 @@ def ErrorBoundary(
|
|
|
1600
2036
|
|
|
1601
2037
|
|
|
1602
2038
|
# ======================================================================
|
|
1603
|
-
# Lists (Python-windowed
|
|
2039
|
+
# Lists (native virtualization with a Python-windowed fallback)
|
|
1604
2040
|
# ======================================================================
|
|
1605
2041
|
#
|
|
1606
|
-
# FlatList and SectionList
|
|
1607
|
-
#
|
|
1608
|
-
#
|
|
1609
|
-
#
|
|
1610
|
-
#
|
|
1611
|
-
#
|
|
1612
|
-
#
|
|
1613
|
-
#
|
|
2042
|
+
# FlatList and SectionList pick between two engines:
|
|
2043
|
+
#
|
|
2044
|
+
# 1. **Native virtualization** (`_NativeList` -> the ``VirtualList``
|
|
2045
|
+
# element): on Android and iOS, when every row extent is known up
|
|
2046
|
+
# front and no windowed-only feature is requested, the list is
|
|
2047
|
+
# backed by a real ``RecyclerView`` / ``UITableView``. The platform
|
|
2048
|
+
# owns row recycling; each visible row hosts a nested-reconciler
|
|
2049
|
+
# subtree (see ``pythonnative.virtual_rows``).
|
|
2050
|
+
# 2. **Python windowing** (`_VirtualizedList`): a windowed slice of
|
|
2051
|
+
# rows rendered into a ScrollView (leading spacer, visible rows,
|
|
2052
|
+
# trailing spacer), the window shifting from scroll events (the
|
|
2053
|
+
# same architecture as React Native's VirtualizedList). Because
|
|
2054
|
+
# every windowed row lives in the *main* layout tree, rows may be
|
|
2055
|
+
# any height: estimates steer the spacer sizes and measured extents
|
|
2056
|
+
# correct them over time. This is the desktop path and the fallback
|
|
2057
|
+
# for variable-height rows, grids, horizontal lists, ornaments, and
|
|
2058
|
+
# pull-to-refresh.
|
|
1614
2059
|
|
|
1615
2060
|
_DEFAULT_ROW_EXTENT = 44.0
|
|
1616
2061
|
|
|
@@ -1841,6 +2286,132 @@ def _VirtualizedList(**p: Any) -> Element:
|
|
|
1841
2286
|
)
|
|
1842
2287
|
|
|
1843
2288
|
|
|
2289
|
+
def _native_lists_supported() -> bool:
|
|
2290
|
+
"""Whether the natively virtualized list path is available.
|
|
2291
|
+
|
|
2292
|
+
Android (RecyclerView) and iOS (UITableView) have native handlers;
|
|
2293
|
+
the desktop preview and off-device tests use the Python-windowed
|
|
2294
|
+
engine. Patchable in tests to exercise the native routing.
|
|
2295
|
+
"""
|
|
2296
|
+
from .utils import IS_ANDROID, IS_IOS
|
|
2297
|
+
|
|
2298
|
+
return IS_ANDROID or IS_IOS
|
|
2299
|
+
|
|
2300
|
+
|
|
2301
|
+
@component
|
|
2302
|
+
def _NativeList(**p: Any) -> Element:
|
|
2303
|
+
"""Platform-virtualized list: emits a ``VirtualList`` native element.
|
|
2304
|
+
|
|
2305
|
+
The native side (RecyclerView / UITableView) owns row windowing and
|
|
2306
|
+
recycling; each visible row hosts a nested-reconciler subtree (see
|
|
2307
|
+
``pythonnative.virtual_rows``). This composite adapts the FlatList /
|
|
2308
|
+
SectionList surface onto that element: it forwards ``render_row``,
|
|
2309
|
+
derives ``on_end_reached`` and ``on_viewable_items_changed`` from
|
|
2310
|
+
native scroll reports, and wires the imperative scroll controller.
|
|
2311
|
+
|
|
2312
|
+
Requires every row's extent to be known up front (the native
|
|
2313
|
+
virtualizers need exact heights before rows are rendered); callers
|
|
2314
|
+
fall back to the Python-windowed engine otherwise.
|
|
2315
|
+
"""
|
|
2316
|
+
rows: List[_RowSpec] = p.get("rows") or []
|
|
2317
|
+
n = len(rows)
|
|
2318
|
+
heights: List[float] = [float(spec.extent or 0.0) for spec in rows]
|
|
2319
|
+
uniform = len(set(heights)) <= 1
|
|
2320
|
+
|
|
2321
|
+
internal_ref = use_ref(None)
|
|
2322
|
+
end_latch = use_ref({"fired_for": -1})
|
|
2323
|
+
viewable_ref = use_ref({"keys": ()})
|
|
2324
|
+
|
|
2325
|
+
starts: List[float] = [0.0] * (n + 1)
|
|
2326
|
+
acc = 0.0
|
|
2327
|
+
for i, extent in enumerate(heights):
|
|
2328
|
+
starts[i] = acc
|
|
2329
|
+
acc += max(0.0, extent)
|
|
2330
|
+
starts[n] = acc
|
|
2331
|
+
total_extent = acc
|
|
2332
|
+
|
|
2333
|
+
def _render_row(index: int) -> Element:
|
|
2334
|
+
if 0 <= index < n:
|
|
2335
|
+
return rows[index].make()
|
|
2336
|
+
return View()
|
|
2337
|
+
|
|
2338
|
+
on_end_reached = p.get("on_end_reached")
|
|
2339
|
+
end_threshold = float(p.get("on_end_reached_threshold") or 0.5)
|
|
2340
|
+
on_viewable = p.get("on_viewable_items_changed")
|
|
2341
|
+
user_on_scroll = p.get("on_scroll")
|
|
2342
|
+
|
|
2343
|
+
def _handle_scroll(payload: Any) -> None:
|
|
2344
|
+
offset = float(payload.get("y", 0.0) or 0.0) if isinstance(payload, dict) else float(payload or 0.0)
|
|
2345
|
+
viewport = float(payload.get("extent", 0.0) or 0.0) if isinstance(payload, dict) else 0.0
|
|
2346
|
+
if viewport <= 0:
|
|
2347
|
+
viewport = 800.0
|
|
2348
|
+
|
|
2349
|
+
if on_end_reached is not None and total_extent > 0:
|
|
2350
|
+
remaining = total_extent - (offset + viewport)
|
|
2351
|
+
if remaining <= end_threshold * viewport:
|
|
2352
|
+
if end_latch["current"]["fired_for"] != n:
|
|
2353
|
+
end_latch["current"]["fired_for"] = n
|
|
2354
|
+
on_end_reached()
|
|
2355
|
+
elif remaining > end_threshold * viewport + viewport:
|
|
2356
|
+
end_latch["current"]["fired_for"] = -1
|
|
2357
|
+
|
|
2358
|
+
if on_viewable is not None and n > 0:
|
|
2359
|
+
v_first = max(0, bisect.bisect_right(starts, offset, 0, n) - 1)
|
|
2360
|
+
v_last = min(n - 1, bisect.bisect_left(starts, offset + viewport, 0, n))
|
|
2361
|
+
keys = tuple(rows[i].key for i in range(v_first, v_last + 1))
|
|
2362
|
+
if keys != viewable_ref["current"]["keys"]:
|
|
2363
|
+
viewable_ref["current"]["keys"] = keys
|
|
2364
|
+
on_viewable(
|
|
2365
|
+
[
|
|
2366
|
+
{"index": rows[i].index, "key": rows[i].key, "item": rows[i].item}
|
|
2367
|
+
for i in range(v_first, v_last + 1)
|
|
2368
|
+
]
|
|
2369
|
+
)
|
|
2370
|
+
|
|
2371
|
+
if user_on_scroll is not None:
|
|
2372
|
+
user_on_scroll({"x": 0.0, "y": offset})
|
|
2373
|
+
|
|
2374
|
+
controller = p.get("controller_ref")
|
|
2375
|
+
|
|
2376
|
+
def _attach_controller() -> None:
|
|
2377
|
+
if not isinstance(controller, dict):
|
|
2378
|
+
return
|
|
2379
|
+
|
|
2380
|
+
def scroll_to_offset(offset: float, animated: bool = True) -> None:
|
|
2381
|
+
_dispatch_scroll_command(internal_ref, "scroll_to_offset", {"y": float(offset), "animated": animated})
|
|
2382
|
+
|
|
2383
|
+
def scroll_to_index(index: int, animated: bool = True) -> None:
|
|
2384
|
+
_dispatch_scroll_command(internal_ref, "scroll_to_index", {"index": int(index), "animated": animated})
|
|
2385
|
+
|
|
2386
|
+
def scroll_to_end(animated: bool = True) -> None:
|
|
2387
|
+
_dispatch_scroll_command(internal_ref, "scroll_to_end", {"animated": animated})
|
|
2388
|
+
|
|
2389
|
+
controller["scroll_to_offset"] = scroll_to_offset
|
|
2390
|
+
controller["scroll_to_index"] = scroll_to_index
|
|
2391
|
+
controller["scroll_to_end"] = scroll_to_end
|
|
2392
|
+
|
|
2393
|
+
use_effect(_attach_controller, None)
|
|
2394
|
+
|
|
2395
|
+
props: Dict[str, Any] = dict(p.get("list_style") or {})
|
|
2396
|
+
props["count"] = n
|
|
2397
|
+
if uniform:
|
|
2398
|
+
props["row_height"] = heights[0] if heights else _DEFAULT_ROW_EXTENT
|
|
2399
|
+
else:
|
|
2400
|
+
props["row_heights"] = heights
|
|
2401
|
+
props["render_row"] = _render_row
|
|
2402
|
+
props["ref"] = internal_ref
|
|
2403
|
+
wants_scroll = on_end_reached is not None or on_viewable is not None or user_on_scroll is not None
|
|
2404
|
+
if wants_scroll:
|
|
2405
|
+
props["on_scroll"] = _handle_scroll
|
|
2406
|
+
if p.get("shows_scroll_indicator") is False:
|
|
2407
|
+
props["shows_scroll_indicator"] = False
|
|
2408
|
+
return Element("VirtualList", props, [])
|
|
2409
|
+
|
|
2410
|
+
|
|
2411
|
+
def _all_extents_known(rows: List[_RowSpec]) -> bool:
|
|
2412
|
+
return all(spec.extent is not None for spec in rows)
|
|
2413
|
+
|
|
2414
|
+
|
|
1844
2415
|
def FlatList(
|
|
1845
2416
|
*,
|
|
1846
2417
|
data: Optional[List[Any]] = None,
|
|
@@ -1994,6 +2565,32 @@ def FlatList(
|
|
|
1994
2565
|
|
|
1995
2566
|
estimated = estimated_item_height if estimated_item_height is not None else (item_height or _DEFAULT_ROW_EXTENT)
|
|
1996
2567
|
|
|
2568
|
+
# Route to the platform virtualizer (RecyclerView / UITableView)
|
|
2569
|
+
# when it can represent this list exactly: vertical, single-column,
|
|
2570
|
+
# every row extent known up front, and no features that only the
|
|
2571
|
+
# Python-windowed engine implements (ornaments, pull-to-refresh).
|
|
2572
|
+
if (
|
|
2573
|
+
_native_lists_supported()
|
|
2574
|
+
and not horizontal
|
|
2575
|
+
and num_columns == 1
|
|
2576
|
+
and list_header is None
|
|
2577
|
+
and list_footer is None
|
|
2578
|
+
and list_empty is None
|
|
2579
|
+
and refresh_control is None
|
|
2580
|
+
and _all_extents_known(rows)
|
|
2581
|
+
):
|
|
2582
|
+
return _NativeList(
|
|
2583
|
+
rows=rows,
|
|
2584
|
+
on_end_reached=on_end_reached,
|
|
2585
|
+
on_end_reached_threshold=on_end_reached_threshold,
|
|
2586
|
+
on_viewable_items_changed=on_viewable_items_changed,
|
|
2587
|
+
on_scroll=on_scroll,
|
|
2588
|
+
shows_scroll_indicator=shows_scroll_indicator,
|
|
2589
|
+
list_style=resolve_style(style) or None,
|
|
2590
|
+
controller_ref=ref,
|
|
2591
|
+
key=key,
|
|
2592
|
+
)
|
|
2593
|
+
|
|
1997
2594
|
return _VirtualizedList(
|
|
1998
2595
|
rows=rows,
|
|
1999
2596
|
horizontal=horizontal,
|
|
@@ -2132,6 +2729,26 @@ def SectionList(
|
|
|
2132
2729
|
|
|
2133
2730
|
estimated = estimated_item_height if estimated_item_height is not None else (item_height or _DEFAULT_ROW_EXTENT)
|
|
2134
2731
|
|
|
2732
|
+
# Same native routing as FlatList: headers and items become one
|
|
2733
|
+
# flattened row sequence with per-row heights.
|
|
2734
|
+
if (
|
|
2735
|
+
_native_lists_supported()
|
|
2736
|
+
and list_header is None
|
|
2737
|
+
and list_footer is None
|
|
2738
|
+
and list_empty is None
|
|
2739
|
+
and refresh_control is None
|
|
2740
|
+
and _all_extents_known(rows)
|
|
2741
|
+
):
|
|
2742
|
+
return _NativeList(
|
|
2743
|
+
rows=rows,
|
|
2744
|
+
on_end_reached=on_end_reached,
|
|
2745
|
+
on_end_reached_threshold=on_end_reached_threshold,
|
|
2746
|
+
on_scroll=on_scroll,
|
|
2747
|
+
list_style=resolve_style(style) or None,
|
|
2748
|
+
controller_ref=ref,
|
|
2749
|
+
key=key,
|
|
2750
|
+
)
|
|
2751
|
+
|
|
2135
2752
|
return _VirtualizedList(
|
|
2136
2753
|
rows=rows,
|
|
2137
2754
|
horizontal=False,
|
|
@@ -2194,9 +2811,40 @@ def StatusBar(
|
|
|
2194
2811
|
return Element("StatusBar", props, [], key=key)
|
|
2195
2812
|
|
|
2196
2813
|
|
|
2814
|
+
@component
|
|
2815
|
+
def _KeyboardAvoidingContainer(**p: Any) -> Element:
|
|
2816
|
+
"""Hook-driven body of `KeyboardAvoidingView`.
|
|
2817
|
+
|
|
2818
|
+
Subscribes to the platform-reported keyboard height via
|
|
2819
|
+
[`use_keyboard_height`][pythonnative.use_keyboard_height] and
|
|
2820
|
+
applies the shift according to ``behavior``:
|
|
2821
|
+
|
|
2822
|
+
- ``"padding"``: adds the shift as bottom padding, resizing the
|
|
2823
|
+
content area (the default, and the right choice for forms
|
|
2824
|
+
inside a full-height container).
|
|
2825
|
+
- ``"position"``: translates the whole container upward without
|
|
2826
|
+
resizing it (useful for pinned footers/toolbars).
|
|
2827
|
+
"""
|
|
2828
|
+
height = use_keyboard_height()
|
|
2829
|
+
behavior = p.get("behavior") or "padding"
|
|
2830
|
+
offset = float(p.get("keyboard_vertical_offset") or 0.0)
|
|
2831
|
+
shift = max(0.0, height - offset) if height > 0 else 0.0
|
|
2832
|
+
style: Dict[str, Any] = dict(p.get("style") or {})
|
|
2833
|
+
if shift > 0:
|
|
2834
|
+
if behavior == "position":
|
|
2835
|
+
transform = list(style.get("transform") or [])
|
|
2836
|
+
transform.append({"translate_y": -shift})
|
|
2837
|
+
style["transform"] = transform
|
|
2838
|
+
else:
|
|
2839
|
+
style["padding_bottom"] = _numeric_edge_padding(style, "bottom") + shift
|
|
2840
|
+
children = p.get("children") or []
|
|
2841
|
+
return Element("KeyboardAvoidingView", style, list(children))
|
|
2842
|
+
|
|
2843
|
+
|
|
2197
2844
|
def KeyboardAvoidingView(
|
|
2198
2845
|
*children: Element,
|
|
2199
2846
|
behavior: Literal["padding", "position"] = "padding",
|
|
2847
|
+
keyboard_vertical_offset: float = 0.0,
|
|
2200
2848
|
style: StyleProp = None,
|
|
2201
2849
|
key: Optional[str] = None,
|
|
2202
2850
|
) -> Element:
|
|
@@ -2204,26 +2852,32 @@ def KeyboardAvoidingView(
|
|
|
2204
2852
|
|
|
2205
2853
|
Subscribes to the platform-reported keyboard height (via
|
|
2206
2854
|
[`use_keyboard_height`][pythonnative.use_keyboard_height]
|
|
2207
|
-
internally) and
|
|
2208
|
-
|
|
2855
|
+
internally) and shifts its content so the focused text input stays
|
|
2856
|
+
visible. On iOS the height comes from
|
|
2857
|
+
``UIKeyboardWillShowNotification``; on Android from the window's
|
|
2858
|
+
IME insets.
|
|
2209
2859
|
|
|
2210
2860
|
Args:
|
|
2211
2861
|
*children: Children rendered inside the avoiding container.
|
|
2212
|
-
behavior: ``"padding"`` (adds bottom padding
|
|
2213
|
-
(translates the container
|
|
2862
|
+
behavior: ``"padding"`` (adds bottom padding, resizing the
|
|
2863
|
+
content) or ``"position"`` (translates the container
|
|
2864
|
+
upward without resizing).
|
|
2865
|
+
keyboard_vertical_offset: Distance in layout units already
|
|
2866
|
+
covered by other UI (e.g. a nav bar); subtracted from the
|
|
2867
|
+
keyboard height before applying the shift.
|
|
2214
2868
|
style: Style dict (or list of dicts).
|
|
2215
2869
|
key: Stable identity for keyed reconciliation.
|
|
2216
2870
|
|
|
2217
2871
|
Returns:
|
|
2218
|
-
An [`Element`][pythonnative.Element]
|
|
2219
|
-
``"KeyboardAvoidingView"
|
|
2872
|
+
An [`Element`][pythonnative.Element] that renders a
|
|
2873
|
+
``"KeyboardAvoidingView"`` container.
|
|
2220
2874
|
"""
|
|
2221
|
-
return
|
|
2222
|
-
|
|
2223
|
-
*children,
|
|
2224
|
-
style=style,
|
|
2225
|
-
key=key,
|
|
2875
|
+
return _KeyboardAvoidingContainer(
|
|
2876
|
+
children=list(children),
|
|
2226
2877
|
behavior=behavior,
|
|
2878
|
+
keyboard_vertical_offset=keyboard_vertical_offset,
|
|
2879
|
+
style=resolve_style(style),
|
|
2880
|
+
key=key,
|
|
2227
2881
|
)
|
|
2228
2882
|
|
|
2229
2883
|
|
|
@@ -2291,6 +2945,9 @@ def Picker(
|
|
|
2291
2945
|
accessibility_label: Optional[str] = None,
|
|
2292
2946
|
accessibility_hint: Optional[str] = None,
|
|
2293
2947
|
accessible: Optional[bool] = None,
|
|
2948
|
+
accessibility_state: Optional[Dict[str, Any]] = None,
|
|
2949
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None,
|
|
2950
|
+
test_id: Optional[str] = None,
|
|
2294
2951
|
ref: Optional[Dict[str, Any]] = None,
|
|
2295
2952
|
key: Optional[str] = None,
|
|
2296
2953
|
) -> Element:
|
|
@@ -2314,6 +2971,16 @@ def Picker(
|
|
|
2314
2971
|
accessibility_label: Spoken description for screen readers.
|
|
2315
2972
|
accessibility_hint: Spoken extra detail (iOS only).
|
|
2316
2973
|
accessible: Override whether the element is exposed to AT.
|
|
2974
|
+
accessibility_state: Current widget state for assistive tech,
|
|
2975
|
+
e.g. ``{"disabled": True, "selected": False}``. Recognized
|
|
2976
|
+
keys: ``disabled``, ``selected``, ``checked``, ``busy``,
|
|
2977
|
+
``expanded``.
|
|
2978
|
+
accessibility_live_region: How AT announces dynamic changes to
|
|
2979
|
+
this view: ``"none"``, ``"polite"``, or ``"assertive"``
|
|
2980
|
+
(Android only).
|
|
2981
|
+
test_id: Stable identifier for UI tests; exposed as
|
|
2982
|
+
``resource-id`` on Android and ``accessibilityIdentifier``
|
|
2983
|
+
on iOS.
|
|
2317
2984
|
ref: Optional ``use_ref()`` dict.
|
|
2318
2985
|
key: Stable identity for keyed reconciliation.
|
|
2319
2986
|
|
|
@@ -2332,5 +2999,8 @@ def Picker(
|
|
|
2332
2999
|
accessibility_label=accessibility_label,
|
|
2333
3000
|
accessibility_hint=accessibility_hint,
|
|
2334
3001
|
accessible=accessible,
|
|
3002
|
+
accessibility_state=accessibility_state,
|
|
3003
|
+
accessibility_live_region=accessibility_live_region,
|
|
3004
|
+
test_id=test_id,
|
|
2335
3005
|
_defaults={"accessibility_role": "button"},
|
|
2336
3006
|
)
|