pythonnative 0.24.0__py3-none-any.whl → 0.26.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 -4
- pythonnative/animated.py +2 -2
- pythonnative/cli/pn.py +172 -12
- pythonnative/components.py +59 -0
- pythonnative/diagnostics.py +27 -0
- pythonnative/hooks.py +364 -81
- pythonnative/native_modules/app_state.py +2 -1
- pythonnative/native_modules/battery.py +3 -2
- pythonnative/native_modules/biometrics.py +2 -1
- pythonnative/native_modules/camera.py +16 -7
- pythonnative/native_modules/clipboard.py +3 -2
- pythonnative/native_modules/file_system.py +3 -2
- pythonnative/native_modules/haptics.py +7 -6
- pythonnative/native_modules/linking.py +62 -6
- pythonnative/native_modules/location.py +11 -8
- pythonnative/native_modules/net_info.py +2 -1
- pythonnative/native_modules/notifications.py +132 -9
- pythonnative/native_modules/permissions.py +27 -3
- pythonnative/native_modules/share.py +2 -1
- pythonnative/native_views/android.py +149 -124
- pythonnative/native_views/desktop.py +50 -49
- pythonnative/native_views/ios.py +240 -161
- pythonnative/project/android.py +15 -3
- pythonnative/project/builder.py +172 -70
- pythonnative/project/config.py +28 -6
- pythonnative/project/devices.py +291 -0
- pythonnative/project/doctor.py +5 -4
- pythonnative/project/ios.py +51 -77
- pythonnative/project/permissions.py +19 -1
- pythonnative/project/runtime_assets.py +78 -166
- pythonnative/reconciler.py +518 -37
- pythonnative/runtime.py +407 -215
- pythonnative/screen.py +53 -7
- pythonnative/storage.py +2 -2
- pythonnative/suspense.py +417 -0
- pythonnative/templates/android_template/app/build.gradle +5 -0
- pythonnative/templates/android_template/app/src/main/AndroidManifest.xml +1 -0
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +88 -12
- pythonnative/templates/ios_template/ios_template/AppDelegate.swift +41 -17
- pythonnative/templates/ios_template/ios_template/BridgingHeader.h +14 -0
- pythonnative/templates/ios_template/ios_template/Info.plist +0 -2
- pythonnative/templates/ios_template/ios_template/PythonRuntime.swift +397 -0
- pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +31 -20
- pythonnative/templates/ios_template/ios_template/ViewController.swift +77 -198
- pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +72 -47
- {pythonnative-0.24.0.dist-info → pythonnative-0.26.0.dist-info}/METADATA +2 -1
- {pythonnative-0.24.0.dist-info → pythonnative-0.26.0.dist-info}/RECORD +51 -48
- {pythonnative-0.24.0.dist-info → pythonnative-0.26.0.dist-info}/WHEEL +1 -1
- pythonnative/templates/ios_template/ios_template/Base.lproj/Main.storyboard +0 -24
- {pythonnative-0.24.0.dist-info → pythonnative-0.26.0.dist-info}/entry_points.txt +0 -0
- {pythonnative-0.24.0.dist-info → pythonnative-0.26.0.dist-info}/licenses/LICENSE +0 -0
- {pythonnative-0.24.0.dist-info → pythonnative-0.26.0.dist-info}/top_level.txt +0 -0
pythonnative/native_views/ios.py
CHANGED
|
@@ -42,6 +42,7 @@ from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
|
42
42
|
|
|
43
43
|
from rubicon.objc import SEL, ObjCClass, ObjCInstance, objc_method
|
|
44
44
|
|
|
45
|
+
from .. import diagnostics
|
|
45
46
|
from ..events import dispatch_event, event_names
|
|
46
47
|
from . import _tripwire_log
|
|
47
48
|
from .base import ViewHandler, _safe_max, parse_color_int
|
|
@@ -80,7 +81,7 @@ try:
|
|
|
80
81
|
_UIView = ObjCClass("UIView")
|
|
81
82
|
_UIView.declare_property("superview")
|
|
82
83
|
except Exception:
|
|
83
|
-
|
|
84
|
+
diagnostics.swallowed("ios.<module>")
|
|
84
85
|
|
|
85
86
|
|
|
86
87
|
def _objc_ptr(obj: Any) -> Optional[int]:
|
|
@@ -267,17 +268,17 @@ def _apply_border(layer: Any, props: Dict[str, Any]) -> None:
|
|
|
267
268
|
# asks for corners (matches iOS UIKit common practice).
|
|
268
269
|
layer.setMasksToBounds_(True)
|
|
269
270
|
except Exception:
|
|
270
|
-
|
|
271
|
+
diagnostics.swallowed("ios._apply_border")
|
|
271
272
|
if "border_width" in props and props["border_width"] is not None:
|
|
272
273
|
try:
|
|
273
274
|
layer.setBorderWidth_(float(props["border_width"]))
|
|
274
275
|
except Exception:
|
|
275
|
-
|
|
276
|
+
diagnostics.swallowed("ios._apply_border")
|
|
276
277
|
if "border_color" in props and props["border_color"] is not None:
|
|
277
278
|
try:
|
|
278
279
|
layer.setBorderColor_(_cgcolor(props["border_color"]))
|
|
279
280
|
except Exception:
|
|
280
|
-
|
|
281
|
+
diagnostics.swallowed("ios._apply_border")
|
|
281
282
|
|
|
282
283
|
|
|
283
284
|
def _apply_view_border(view: Any, props: Dict[str, Any]) -> None:
|
|
@@ -294,13 +295,13 @@ def _apply_view_border(view: Any, props: Dict[str, Any]) -> None:
|
|
|
294
295
|
if width > 0.0 and height > 0.0:
|
|
295
296
|
radius = min(requested, min(width, height) / 2.0)
|
|
296
297
|
except Exception:
|
|
297
|
-
|
|
298
|
+
diagnostics.swallowed("ios._apply_view_border")
|
|
298
299
|
border_props = dict(props)
|
|
299
300
|
border_props["border_radius"] = radius
|
|
300
301
|
_apply_border(view.layer, border_props)
|
|
301
302
|
return
|
|
302
303
|
except Exception:
|
|
303
|
-
|
|
304
|
+
diagnostics.swallowed("ios._apply_view_border")
|
|
304
305
|
_apply_border(view.layer, props)
|
|
305
306
|
|
|
306
307
|
|
|
@@ -344,7 +345,7 @@ def _apply_side_borders(view: Any, props: Dict[str, Any]) -> None:
|
|
|
344
345
|
try:
|
|
345
346
|
view.layer.setBorderWidth_(0.0)
|
|
346
347
|
except Exception:
|
|
347
|
-
|
|
348
|
+
diagnostics.swallowed("ios._apply_side_borders")
|
|
348
349
|
try:
|
|
349
350
|
bounds = view.bounds
|
|
350
351
|
w = float(bounds.size.width)
|
|
@@ -352,7 +353,7 @@ def _apply_side_borders(view: Any, props: Dict[str, Any]) -> None:
|
|
|
352
353
|
if w > 0.0 and h > 0.0:
|
|
353
354
|
_update_side_border_layers(view, w, h)
|
|
354
355
|
except Exception:
|
|
355
|
-
|
|
356
|
+
diagnostics.swallowed("ios._apply_side_borders")
|
|
356
357
|
|
|
357
358
|
|
|
358
359
|
def _update_side_border_layers(view: Any, width: float, height: float) -> None:
|
|
@@ -377,7 +378,7 @@ def _update_side_border_layers(view: Any, width: float, height: float) -> None:
|
|
|
377
378
|
try:
|
|
378
379
|
layer.removeFromSuperlayer()
|
|
379
380
|
except Exception:
|
|
380
|
-
|
|
381
|
+
diagnostics.swallowed("ios._update_side_border_layers")
|
|
381
382
|
layers[i] = None
|
|
382
383
|
continue
|
|
383
384
|
try:
|
|
@@ -388,7 +389,7 @@ def _update_side_border_layers(view: Any, width: float, height: float) -> None:
|
|
|
388
389
|
layer.setBackgroundColor_(_cgcolor(entry["colors"][i]))
|
|
389
390
|
layer.setFrame_(frames[i])
|
|
390
391
|
except Exception:
|
|
391
|
-
|
|
392
|
+
diagnostics.swallowed("ios._update_side_border_layers")
|
|
392
393
|
|
|
393
394
|
|
|
394
395
|
def _clamp_view_corner_radius(view: Any, width: float, height: float) -> None:
|
|
@@ -402,7 +403,7 @@ def _clamp_view_corner_radius(view: Any, width: float, height: float) -> None:
|
|
|
402
403
|
try:
|
|
403
404
|
view.layer.setCornerRadius_(min(float(requested), max_radius))
|
|
404
405
|
except Exception:
|
|
405
|
-
|
|
406
|
+
diagnostics.swallowed("ios._clamp_view_corner_radius")
|
|
406
407
|
|
|
407
408
|
|
|
408
409
|
def _clamp_layer_corner_radius(layer: Any, width: float, height: float) -> None:
|
|
@@ -418,7 +419,7 @@ def _clamp_layer_corner_radius(layer: Any, width: float, height: float) -> None:
|
|
|
418
419
|
try:
|
|
419
420
|
layer.setCornerRadius_(min(requested, max_radius))
|
|
420
421
|
except Exception:
|
|
421
|
-
|
|
422
|
+
diagnostics.swallowed("ios._clamp_layer_corner_radius")
|
|
422
423
|
|
|
423
424
|
|
|
424
425
|
def _apply_shadow(view: Any, props: Dict[str, Any]) -> None:
|
|
@@ -434,22 +435,22 @@ def _apply_shadow(view: Any, props: Dict[str, Any]) -> None:
|
|
|
434
435
|
layer.setMasksToBounds_(False)
|
|
435
436
|
view.setClipsToBounds_(False)
|
|
436
437
|
except Exception:
|
|
437
|
-
|
|
438
|
+
diagnostics.swallowed("ios._apply_shadow")
|
|
438
439
|
if "shadow_color" in props and props["shadow_color"] is not None:
|
|
439
440
|
try:
|
|
440
441
|
layer.setShadowColor_(_cgcolor(props["shadow_color"]))
|
|
441
442
|
except Exception:
|
|
442
|
-
|
|
443
|
+
diagnostics.swallowed("ios._apply_shadow")
|
|
443
444
|
if "shadow_opacity" in props and props["shadow_opacity"] is not None:
|
|
444
445
|
try:
|
|
445
446
|
layer.setShadowOpacity_(float(props["shadow_opacity"]))
|
|
446
447
|
except Exception:
|
|
447
|
-
|
|
448
|
+
diagnostics.swallowed("ios._apply_shadow")
|
|
448
449
|
if "shadow_radius" in props and props["shadow_radius"] is not None:
|
|
449
450
|
try:
|
|
450
451
|
layer.setShadowRadius_(float(props["shadow_radius"]))
|
|
451
452
|
except Exception:
|
|
452
|
-
|
|
453
|
+
diagnostics.swallowed("ios._apply_shadow")
|
|
453
454
|
if "shadow_offset" in props and props["shadow_offset"] is not None:
|
|
454
455
|
offset = props["shadow_offset"]
|
|
455
456
|
try:
|
|
@@ -460,7 +461,7 @@ def _apply_shadow(view: Any, props: Dict[str, Any]) -> None:
|
|
|
460
461
|
w, h = float(offset[0]), float(offset[1])
|
|
461
462
|
layer.setShadowOffset_((w, h))
|
|
462
463
|
except Exception:
|
|
463
|
-
|
|
464
|
+
diagnostics.swallowed("ios._apply_shadow")
|
|
464
465
|
|
|
465
466
|
|
|
466
467
|
def _make_transform(spec: Any) -> Any:
|
|
@@ -534,7 +535,7 @@ def _apply_transform(view: Any, props: Dict[str, Any]) -> None:
|
|
|
534
535
|
try:
|
|
535
536
|
view.setTransform_((1.0, 0.0, 0.0, 1.0, 0.0, 0.0))
|
|
536
537
|
except Exception:
|
|
537
|
-
|
|
538
|
+
diagnostics.swallowed("ios._apply_transform")
|
|
538
539
|
return
|
|
539
540
|
try:
|
|
540
541
|
transform = _make_transform(spec)
|
|
@@ -565,7 +566,7 @@ def _apply_transform(view: Any, props: Dict[str, Any]) -> None:
|
|
|
565
566
|
# rubicon-objc accepts the C struct as a tuple of its fields.
|
|
566
567
|
view.setTransform_((a, b, c, d, tx, ty))
|
|
567
568
|
except Exception:
|
|
568
|
-
|
|
569
|
+
diagnostics.swallowed("ios._apply_transform")
|
|
569
570
|
|
|
570
571
|
|
|
571
572
|
# UIAccessibilityTraits bitmask values (UIKit constants).
|
|
@@ -604,25 +605,25 @@ def _apply_accessibility(view: Any, props: Dict[str, Any]) -> None:
|
|
|
604
605
|
try:
|
|
605
606
|
view.setIsAccessibilityElement_(bool(props["accessible"]))
|
|
606
607
|
except Exception:
|
|
607
|
-
|
|
608
|
+
diagnostics.swallowed("ios._apply_accessibility")
|
|
608
609
|
if "accessibility_label" in props:
|
|
609
610
|
v = props["accessibility_label"]
|
|
610
611
|
try:
|
|
611
612
|
view.setAccessibilityLabel_(str(v) if v is not None else "")
|
|
612
613
|
except Exception:
|
|
613
|
-
|
|
614
|
+
diagnostics.swallowed("ios._apply_accessibility")
|
|
614
615
|
if "accessibility_hint" in props:
|
|
615
616
|
v = props["accessibility_hint"]
|
|
616
617
|
try:
|
|
617
618
|
view.setAccessibilityHint_(str(v) if v is not None else "")
|
|
618
619
|
except Exception:
|
|
619
|
-
|
|
620
|
+
diagnostics.swallowed("ios._apply_accessibility")
|
|
620
621
|
if "test_id" in props:
|
|
621
622
|
v = props["test_id"]
|
|
622
623
|
try:
|
|
623
624
|
view.setAccessibilityIdentifier_(str(v) if v is not None else None)
|
|
624
625
|
except Exception:
|
|
625
|
-
|
|
626
|
+
diagnostics.swallowed("ios._apply_accessibility")
|
|
626
627
|
|
|
627
628
|
role = props.get("accessibility_role")
|
|
628
629
|
state = props.get("accessibility_state")
|
|
@@ -642,11 +643,11 @@ def _apply_accessibility(view: Any, props: Dict[str, Any]) -> None:
|
|
|
642
643
|
try:
|
|
643
644
|
view.setAccessibilityValue_("expanded" if state["expanded"] else "collapsed")
|
|
644
645
|
except Exception:
|
|
645
|
-
|
|
646
|
+
diagnostics.swallowed("ios._apply_accessibility")
|
|
646
647
|
try:
|
|
647
648
|
view.setAccessibilityTraits_(traits)
|
|
648
649
|
except Exception:
|
|
649
|
-
|
|
650
|
+
diagnostics.swallowed("ios._apply_accessibility")
|
|
650
651
|
|
|
651
652
|
|
|
652
653
|
def _apply_common_visual(view: Any, props: Dict[str, Any]) -> None:
|
|
@@ -657,14 +658,14 @@ def _apply_common_visual(view: Any, props: Dict[str, Any]) -> None:
|
|
|
657
658
|
try:
|
|
658
659
|
view.layer.setBackgroundColor_(color.CGColor)
|
|
659
660
|
except Exception:
|
|
660
|
-
|
|
661
|
+
diagnostics.swallowed("ios._apply_common_visual")
|
|
661
662
|
if "overflow" in props:
|
|
662
663
|
view.setClipsToBounds_(props["overflow"] == "hidden")
|
|
663
664
|
if "opacity" in props and props["opacity"] is not None:
|
|
664
665
|
try:
|
|
665
666
|
view.setAlpha_(float(props["opacity"]))
|
|
666
667
|
except Exception:
|
|
667
|
-
|
|
668
|
+
diagnostics.swallowed("ios._apply_common_visual")
|
|
668
669
|
_apply_view_border(view, props)
|
|
669
670
|
_apply_side_borders(view, props)
|
|
670
671
|
_apply_shadow(view, props)
|
|
@@ -733,7 +734,7 @@ def _set_recognizer_delegate(recognizer: Any) -> None:
|
|
|
733
734
|
rec_ptr = recognizer.ptr if hasattr(recognizer, "ptr") else recognizer
|
|
734
735
|
_objc_msgSend(rec_ptr, _SEL_SET_DELEGATE, delegate)
|
|
735
736
|
except Exception:
|
|
736
|
-
|
|
737
|
+
diagnostics.swallowed("ios._set_recognizer_delegate")
|
|
737
738
|
|
|
738
739
|
|
|
739
740
|
# ======================================================================
|
|
@@ -753,6 +754,7 @@ def _set_recognizer_delegate(recognizer: Any) -> None:
|
|
|
753
754
|
_pn_action_handlers: Dict[int, Any] = {}
|
|
754
755
|
|
|
755
756
|
_ACTION_IMP_TYPE = _ct.CFUNCTYPE(None, _ct.c_void_p, _ct.c_void_p, _ct.c_void_p)
|
|
757
|
+
_CONTROL_ACTION_IMP_TYPE = _ct.CFUNCTYPE(None, _ct.c_void_p, _ct.c_void_p, _ct.c_void_p, _ct.c_void_p)
|
|
756
758
|
|
|
757
759
|
|
|
758
760
|
def _action_imp(_self_ptr: int, _cmd_ptr: int, sender_ptr: int) -> None:
|
|
@@ -763,12 +765,70 @@ def _action_imp(_self_ptr: int, _cmd_ptr: int, sender_ptr: int) -> None:
|
|
|
763
765
|
try:
|
|
764
766
|
handler()
|
|
765
767
|
except Exception:
|
|
766
|
-
|
|
768
|
+
diagnostics.swallowed("ios._action_imp")
|
|
767
769
|
|
|
768
770
|
|
|
769
771
|
_action_imp_ref = _ACTION_IMP_TYPE(_action_imp)
|
|
770
772
|
|
|
773
|
+
# ----------------------------------------------------------------------
|
|
774
|
+
# UIControl action dedupe
|
|
775
|
+
# ----------------------------------------------------------------------
|
|
776
|
+
#
|
|
777
|
+
# The simulator's XCTest event-injection stack sometimes delivers a
|
|
778
|
+
# single synthesized tap as *two* back-to-back "send control actions"
|
|
779
|
+
# bursts for the same UIEvent (~1ms apart; the unified log shows one
|
|
780
|
+
# UIEvent delivered to one window, then the doubled sends). Every
|
|
781
|
+
# non-idempotent ``on_press`` then fires twice: reducer counters jump
|
|
782
|
+
# by two, alerts present twice, pickers reopen after selecting. The
|
|
783
|
+
# control action selector therefore takes the ``forEvent:`` argument
|
|
784
|
+
# and drops a second send whose UIEvent timestamp matches the last one
|
|
785
|
+
# delivered to the same control. Distinct taps (even rapid ones) carry
|
|
786
|
+
# distinct event timestamps, and continuous ValueChanged streams
|
|
787
|
+
# (slider drags) tick the timestamp per touch move, so only the
|
|
788
|
+
# duplicated sends are filtered. Programmatic ``sendActions...`` calls
|
|
789
|
+
# pass a nil event and are never deduped.
|
|
790
|
+
|
|
791
|
+
# Maps control ptr -> UIEvent.timestamp of the last delivered action.
|
|
792
|
+
_pn_last_control_event_ts: Dict[int, float] = {}
|
|
793
|
+
|
|
794
|
+
_objc_msgSend_double = _ct.CFUNCTYPE(_ct.c_double, _ct.c_void_p, _ct.c_void_p)(("objc_msgSend", _libobjc))
|
|
795
|
+
|
|
796
|
+
_SEL_TIMESTAMP = _sel_reg(b"timestamp")
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
def _control_action_imp(_self_ptr: int, _cmd_ptr: int, sender_ptr: int, event_ptr: int) -> None:
|
|
800
|
+
"""Raw C callback for every PythonNative UIControl event action."""
|
|
801
|
+
key = int(sender_ptr or 0)
|
|
802
|
+
handler = _pn_action_handlers.get(key)
|
|
803
|
+
if handler is None:
|
|
804
|
+
return
|
|
805
|
+
if event_ptr:
|
|
806
|
+
try:
|
|
807
|
+
timestamp = float(_objc_msgSend_double(event_ptr, _SEL_TIMESTAMP))
|
|
808
|
+
except Exception:
|
|
809
|
+
timestamp = None
|
|
810
|
+
if timestamp is not None:
|
|
811
|
+
if _pn_last_control_event_ts.get(key) == timestamp:
|
|
812
|
+
diagnostics.warn_once(
|
|
813
|
+
"ios._control_action_imp dropped a duplicated control action "
|
|
814
|
+
"(one UIEvent, two sends; a simulator event-injection quirk)",
|
|
815
|
+
key=f"control_action_dedupe:{key}",
|
|
816
|
+
)
|
|
817
|
+
return
|
|
818
|
+
_pn_last_control_event_ts[key] = timestamp
|
|
819
|
+
try:
|
|
820
|
+
handler()
|
|
821
|
+
except Exception:
|
|
822
|
+
diagnostics.swallowed("ios._control_action_imp")
|
|
823
|
+
|
|
824
|
+
|
|
825
|
+
_control_action_imp_ref = _CONTROL_ACTION_IMP_TYPE(_control_action_imp)
|
|
826
|
+
|
|
827
|
+
# Gesture recognizers only support zero- or one-argument action
|
|
828
|
+
# selectors, so recognizers keep the plain variant while controls use
|
|
829
|
+
# the two-argument ``forEvent:`` variant for the dedupe above.
|
|
771
830
|
_SEL_ON_ACTION = _sel_reg(b"onPNAction:")
|
|
831
|
+
_SEL_ON_CONTROL_ACTION = _sel_reg(b"onPNControlAction:forEvent:")
|
|
772
832
|
|
|
773
833
|
_PN_ACTION_TARGET_CLS = _alloc_cls(_NS_OBJECT_CLS, b"_PNActionTargetCTypes", 0)
|
|
774
834
|
if _PN_ACTION_TARGET_CLS:
|
|
@@ -778,6 +838,12 @@ if _PN_ACTION_TARGET_CLS:
|
|
|
778
838
|
_ct.cast(_action_imp_ref, _ct.c_void_p),
|
|
779
839
|
b"v@:@",
|
|
780
840
|
)
|
|
841
|
+
_add_method(
|
|
842
|
+
_PN_ACTION_TARGET_CLS,
|
|
843
|
+
_SEL_ON_CONTROL_ACTION,
|
|
844
|
+
_ct.cast(_control_action_imp_ref, _ct.c_void_p),
|
|
845
|
+
b"v@:@@",
|
|
846
|
+
)
|
|
781
847
|
_reg_cls(_PN_ACTION_TARGET_CLS)
|
|
782
848
|
|
|
783
849
|
_pn_action_target_ptr: Any = None
|
|
@@ -830,7 +896,7 @@ def _register_control_action(control: Any, events_mask: int, handler: Any) -> No
|
|
|
830
896
|
_objc_msgSend.restype = None
|
|
831
897
|
_objc_msgSend.argtypes = [_ct.c_void_p, _ct.c_void_p, _ct.c_void_p, _ct.c_ulong]
|
|
832
898
|
ctl_ptr = control.ptr if hasattr(control, "ptr") else control
|
|
833
|
-
_objc_msgSend(ctl_ptr, _SEL_ADD_TARGET_ACTION_EVENTS, target_ptr,
|
|
899
|
+
_objc_msgSend(ctl_ptr, _SEL_ADD_TARGET_ACTION_EVENTS, target_ptr, _SEL_ON_CONTROL_ACTION, events_mask)
|
|
834
900
|
_pn_action_handlers[_recognizer_ptr(control)] = handler
|
|
835
901
|
|
|
836
902
|
|
|
@@ -864,11 +930,11 @@ def _make_gesture_handler(
|
|
|
864
930
|
payload["x"] = float(location.x)
|
|
865
931
|
payload["y"] = float(location.y)
|
|
866
932
|
except Exception:
|
|
867
|
-
|
|
933
|
+
diagnostics.swallowed("ios._make_gesture_handler.handler")
|
|
868
934
|
try:
|
|
869
935
|
payload["pointer_count"] = int(rec.numberOfTouches)
|
|
870
936
|
except Exception:
|
|
871
|
-
|
|
937
|
+
diagnostics.swallowed("ios._make_gesture_handler.handler")
|
|
872
938
|
|
|
873
939
|
if kind == "pan":
|
|
874
940
|
try:
|
|
@@ -879,17 +945,17 @@ def _make_gesture_handler(
|
|
|
879
945
|
payload["velocity_x"] = float(velocity.x)
|
|
880
946
|
payload["velocity_y"] = float(velocity.y)
|
|
881
947
|
except Exception:
|
|
882
|
-
|
|
948
|
+
diagnostics.swallowed("ios._make_gesture_handler.handler")
|
|
883
949
|
elif kind == "pinch":
|
|
884
950
|
try:
|
|
885
951
|
payload["scale"] = float(rec.scale)
|
|
886
952
|
except Exception:
|
|
887
|
-
|
|
953
|
+
diagnostics.swallowed("ios._make_gesture_handler.handler")
|
|
888
954
|
elif kind == "rotation":
|
|
889
955
|
try:
|
|
890
956
|
payload["rotation"] = float(rec.rotation)
|
|
891
957
|
except Exception:
|
|
892
|
-
|
|
958
|
+
diagnostics.swallowed("ios._make_gesture_handler.handler")
|
|
893
959
|
elif kind == "swipe":
|
|
894
960
|
# Discrete: UIKit only calls us on recognition, and only the
|
|
895
961
|
# recognizer whose direction matched fires, so the bound
|
|
@@ -917,7 +983,7 @@ def _make_recognizer(kind: str, spec: Dict[str, Any]) -> List[Tuple[Any, Optiona
|
|
|
917
983
|
try:
|
|
918
984
|
rec.setNumberOfTapsRequired_(max(1, int(spec.get("n_taps", 1))))
|
|
919
985
|
except Exception:
|
|
920
|
-
|
|
986
|
+
diagnostics.swallowed("ios._make_recognizer")
|
|
921
987
|
out.append((rec, None))
|
|
922
988
|
elif kind == "long_press":
|
|
923
989
|
rec = ObjCClass("UILongPressGestureRecognizer").alloc().init()
|
|
@@ -925,14 +991,14 @@ def _make_recognizer(kind: str, spec: Dict[str, Any]) -> List[Tuple[Any, Optiona
|
|
|
925
991
|
rec.setMinimumPressDuration_(float(spec.get("min_duration_ms", 500.0)) / 1000.0)
|
|
926
992
|
rec.setAllowableMovement_(float(spec.get("max_distance", 12.0)))
|
|
927
993
|
except Exception:
|
|
928
|
-
|
|
994
|
+
diagnostics.swallowed("ios._make_recognizer")
|
|
929
995
|
out.append((rec, None))
|
|
930
996
|
elif kind == "pan":
|
|
931
997
|
rec = ObjCClass("UIPanGestureRecognizer").alloc().init()
|
|
932
998
|
try:
|
|
933
999
|
rec.setMinimumNumberOfTouches_(max(1, int(spec.get("min_pointers", 1))))
|
|
934
1000
|
except Exception:
|
|
935
|
-
|
|
1001
|
+
diagnostics.swallowed("ios._make_recognizer")
|
|
936
1002
|
out.append((rec, None))
|
|
937
1003
|
elif kind == "swipe":
|
|
938
1004
|
direction = str(spec.get("direction", "any"))
|
|
@@ -942,7 +1008,7 @@ def _make_recognizer(kind: str, spec: Dict[str, Any]) -> List[Tuple[Any, Optiona
|
|
|
942
1008
|
try:
|
|
943
1009
|
rec.setDirection_(_SWIPE_DIRECTIONS[d])
|
|
944
1010
|
except Exception:
|
|
945
|
-
|
|
1011
|
+
diagnostics.swallowed("ios._make_recognizer")
|
|
946
1012
|
out.append((rec, d))
|
|
947
1013
|
elif kind == "pinch":
|
|
948
1014
|
out.append((ObjCClass("UIPinchGestureRecognizer").alloc().init(), None))
|
|
@@ -963,7 +1029,7 @@ def _wire_gestures(view: Any, specs: Any) -> None:
|
|
|
963
1029
|
try:
|
|
964
1030
|
view.removeGestureRecognizer_(rec)
|
|
965
1031
|
except Exception:
|
|
966
|
-
|
|
1032
|
+
diagnostics.swallowed("ios._wire_gestures")
|
|
967
1033
|
_pn_action_handlers.pop(_recognizer_ptr(rec), None)
|
|
968
1034
|
state["gesture_recognizers"] = []
|
|
969
1035
|
if not isinstance(specs, (list, tuple)) or not specs:
|
|
@@ -972,7 +1038,7 @@ def _wire_gestures(view: Any, specs: Any) -> None:
|
|
|
972
1038
|
try:
|
|
973
1039
|
view.setUserInteractionEnabled_(True)
|
|
974
1040
|
except Exception:
|
|
975
|
-
|
|
1041
|
+
diagnostics.swallowed("ios._wire_gestures")
|
|
976
1042
|
|
|
977
1043
|
recognizers: List[Any] = []
|
|
978
1044
|
for i, spec in enumerate(specs):
|
|
@@ -985,7 +1051,7 @@ def _wire_gestures(view: Any, specs: Any) -> None:
|
|
|
985
1051
|
try:
|
|
986
1052
|
rec.setCancelsTouchesInView_(False)
|
|
987
1053
|
except Exception:
|
|
988
|
-
|
|
1054
|
+
diagnostics.swallowed("ios._wire_gestures")
|
|
989
1055
|
_set_recognizer_delegate(rec)
|
|
990
1056
|
try:
|
|
991
1057
|
view.addGestureRecognizer_(rec)
|
|
@@ -1082,13 +1148,13 @@ def _start_native_animation(view: Any, anim_id: int, prop_name: str, spec: Dict[
|
|
|
1082
1148
|
|
|
1083
1149
|
native_animation_completed(anim_id, bool(finished))
|
|
1084
1150
|
except Exception:
|
|
1085
|
-
|
|
1151
|
+
diagnostics.swallowed("ios._start_native_animation._completion")
|
|
1086
1152
|
|
|
1087
1153
|
def _animations() -> None:
|
|
1088
1154
|
try:
|
|
1089
1155
|
applier_to(view)
|
|
1090
1156
|
except Exception:
|
|
1091
|
-
|
|
1157
|
+
diagnostics.swallowed("ios._start_native_animation._animations")
|
|
1092
1158
|
|
|
1093
1159
|
try:
|
|
1094
1160
|
# Snap to the starting value so the animation covers the full
|
|
@@ -1166,12 +1232,13 @@ class IOSViewHandler(ViewHandler):
|
|
|
1166
1232
|
# Controls register their own pointer as the action-handler key
|
|
1167
1233
|
# (see _register_control_action); drop it with the view.
|
|
1168
1234
|
_pn_action_handlers.pop(_recognizer_ptr(native_view), None)
|
|
1235
|
+
_pn_last_control_event_ts.pop(_recognizer_ptr(native_view), None)
|
|
1169
1236
|
_pn_view_border_radius_map.pop(id(native_view), None)
|
|
1170
1237
|
_pn_side_border_map.pop(id(native_view), None)
|
|
1171
1238
|
try:
|
|
1172
1239
|
native_view.removeFromSuperview()
|
|
1173
1240
|
except Exception:
|
|
1174
|
-
|
|
1241
|
+
diagnostics.swallowed("ios.IOSViewHandler.destroy")
|
|
1175
1242
|
_forget(native_view)
|
|
1176
1243
|
|
|
1177
1244
|
def _build(self, props: Dict[str, Any]) -> Any:
|
|
@@ -1187,7 +1254,7 @@ class IOSViewHandler(ViewHandler):
|
|
|
1187
1254
|
try:
|
|
1188
1255
|
child.setTranslatesAutoresizingMaskIntoConstraints_(True)
|
|
1189
1256
|
except Exception:
|
|
1190
|
-
|
|
1257
|
+
diagnostics.swallowed("ios.IOSViewHandler.insert_child")
|
|
1191
1258
|
try:
|
|
1192
1259
|
count = len(list(parent.subviews or []))
|
|
1193
1260
|
except Exception:
|
|
@@ -1198,13 +1265,13 @@ class IOSViewHandler(ViewHandler):
|
|
|
1198
1265
|
try:
|
|
1199
1266
|
parent.addSubview_(child)
|
|
1200
1267
|
except Exception:
|
|
1201
|
-
|
|
1268
|
+
diagnostics.swallowed("ios.IOSViewHandler.insert_child")
|
|
1202
1269
|
|
|
1203
1270
|
def remove_child(self, parent: Any, child: Any) -> None:
|
|
1204
1271
|
try:
|
|
1205
1272
|
child.removeFromSuperview()
|
|
1206
1273
|
except Exception:
|
|
1207
|
-
|
|
1274
|
+
diagnostics.swallowed("ios.IOSViewHandler.remove_child")
|
|
1208
1275
|
|
|
1209
1276
|
def set_frame(self, native_view: Any, x: float, y: float, width: float, height: float) -> None:
|
|
1210
1277
|
if native_view is None:
|
|
@@ -1221,7 +1288,7 @@ class IOSViewHandler(ViewHandler):
|
|
|
1221
1288
|
try:
|
|
1222
1289
|
_clamp_layer_corner_radius(native_view.layer, frame_w, frame_h)
|
|
1223
1290
|
except Exception:
|
|
1224
|
-
|
|
1291
|
+
diagnostics.swallowed("ios.IOSViewHandler.set_frame")
|
|
1225
1292
|
try:
|
|
1226
1293
|
parent = native_view.superview
|
|
1227
1294
|
parent_cls = ""
|
|
@@ -1238,9 +1305,9 @@ class IOSViewHandler(ViewHandler):
|
|
|
1238
1305
|
content_h = max(float(bounds.size.height), frame_y + frame_h)
|
|
1239
1306
|
parent.setContentSize_((content_w, content_h))
|
|
1240
1307
|
except Exception:
|
|
1241
|
-
|
|
1308
|
+
diagnostics.swallowed("ios.IOSViewHandler.set_frame")
|
|
1242
1309
|
except Exception:
|
|
1243
|
-
|
|
1310
|
+
diagnostics.swallowed("ios.IOSViewHandler.set_frame")
|
|
1244
1311
|
|
|
1245
1312
|
def measure_intrinsic(
|
|
1246
1313
|
self,
|
|
@@ -1273,7 +1340,7 @@ class IOSViewHandler(ViewHandler):
|
|
|
1273
1340
|
try:
|
|
1274
1341
|
applier(native_view)
|
|
1275
1342
|
except Exception:
|
|
1276
|
-
|
|
1343
|
+
diagnostics.swallowed("ios.IOSViewHandler.set_animated_property")
|
|
1277
1344
|
|
|
1278
1345
|
def start_animation(
|
|
1279
1346
|
self,
|
|
@@ -1312,7 +1379,7 @@ class IOSViewHandler(ViewHandler):
|
|
|
1312
1379
|
try:
|
|
1313
1380
|
view.layer.removeAllAnimations()
|
|
1314
1381
|
except Exception:
|
|
1315
|
-
|
|
1382
|
+
diagnostics.swallowed("ios.IOSViewHandler.cancel_animation")
|
|
1316
1383
|
return value
|
|
1317
1384
|
|
|
1318
1385
|
|
|
@@ -1400,7 +1467,7 @@ class TextHandler(IOSViewHandler):
|
|
|
1400
1467
|
if desc is not None:
|
|
1401
1468
|
font = UIFont.fontWithDescriptor_size_(desc, size)
|
|
1402
1469
|
except Exception:
|
|
1403
|
-
|
|
1470
|
+
diagnostics.swallowed("ios.TextHandler._font_for")
|
|
1404
1471
|
return font
|
|
1405
1472
|
|
|
1406
1473
|
_SPAN_REBUILD_KEYS = (
|
|
@@ -1463,7 +1530,7 @@ class TextHandler(IOSViewHandler):
|
|
|
1463
1530
|
try:
|
|
1464
1531
|
label.setAlpha_(float(props["opacity"]))
|
|
1465
1532
|
except Exception:
|
|
1466
|
-
|
|
1533
|
+
diagnostics.swallowed("ios.TextHandler._apply")
|
|
1467
1534
|
|
|
1468
1535
|
def _apply_spans(self, label: Any, merged: Dict[str, Any]) -> None:
|
|
1469
1536
|
"""Render a rich-text span list as one NSAttributedString.
|
|
@@ -1525,13 +1592,13 @@ class TextHandler(IOSViewHandler):
|
|
|
1525
1592
|
elif span.get("text_decoration") == "line_through":
|
|
1526
1593
|
attr.addAttribute_value_range_("NSStrikethrough", 1, rng)
|
|
1527
1594
|
except Exception:
|
|
1528
|
-
|
|
1595
|
+
diagnostics.swallowed("ios.TextHandler._apply_spans")
|
|
1529
1596
|
label.setAttributedText_(attr)
|
|
1530
1597
|
except Exception:
|
|
1531
1598
|
try:
|
|
1532
1599
|
label.setText_(str(merged.get("text") or ""))
|
|
1533
1600
|
except Exception:
|
|
1534
|
-
|
|
1601
|
+
diagnostics.swallowed("ios.TextHandler._apply_spans")
|
|
1535
1602
|
|
|
1536
1603
|
def _apply_attributed(self, label: Any, props: Dict[str, Any]) -> None:
|
|
1537
1604
|
"""Re-render the label's text as an NSAttributedString.
|
|
@@ -1576,7 +1643,7 @@ class TextHandler(IOSViewHandler):
|
|
|
1576
1643
|
attr.addAttribute_value_range_("NSStrikethrough", 1, full_range)
|
|
1577
1644
|
label.setAttributedText_(attr)
|
|
1578
1645
|
except Exception:
|
|
1579
|
-
|
|
1646
|
+
diagnostics.swallowed("ios.TextHandler._apply_attributed")
|
|
1580
1647
|
|
|
1581
1648
|
|
|
1582
1649
|
class ButtonHandler(IOSViewHandler):
|
|
@@ -1632,7 +1699,7 @@ class ButtonHandler(IOSViewHandler):
|
|
|
1632
1699
|
try:
|
|
1633
1700
|
btn.setAlpha_(float(props["opacity"]))
|
|
1634
1701
|
except Exception:
|
|
1635
|
-
|
|
1702
|
+
diagnostics.swallowed("ios.ButtonHandler._apply")
|
|
1636
1703
|
|
|
1637
1704
|
|
|
1638
1705
|
# ``scrollViewDidScroll:`` hands the delegate a ``UIScrollView*``. rubicon's
|
|
@@ -1718,24 +1785,24 @@ class ScrollViewHandler(IOSViewHandler):
|
|
|
1718
1785
|
sv.setShowsVerticalScrollIndicator_(visible)
|
|
1719
1786
|
sv.setShowsHorizontalScrollIndicator_(visible)
|
|
1720
1787
|
except Exception:
|
|
1721
|
-
|
|
1788
|
+
diagnostics.swallowed("ios.ScrollViewHandler._apply")
|
|
1722
1789
|
if "paging_enabled" in props:
|
|
1723
1790
|
try:
|
|
1724
1791
|
sv.setPagingEnabled_(bool(props["paging_enabled"]))
|
|
1725
1792
|
except Exception:
|
|
1726
|
-
|
|
1793
|
+
diagnostics.swallowed("ios.ScrollViewHandler._apply")
|
|
1727
1794
|
if "bounces" in props:
|
|
1728
1795
|
b = props["bounces"]
|
|
1729
1796
|
try:
|
|
1730
1797
|
sv.setBounces_(True if b is None else bool(b))
|
|
1731
1798
|
except Exception:
|
|
1732
|
-
|
|
1799
|
+
diagnostics.swallowed("ios.ScrollViewHandler._apply")
|
|
1733
1800
|
if "keyboard_dismiss_mode" in props and props["keyboard_dismiss_mode"] is not None:
|
|
1734
1801
|
mapping = {"none": 0, "on_drag": 1, "interactive": 2}
|
|
1735
1802
|
try:
|
|
1736
1803
|
sv.setKeyboardDismissMode_(mapping.get(props["keyboard_dismiss_mode"], 0))
|
|
1737
1804
|
except Exception:
|
|
1738
|
-
|
|
1805
|
+
diagnostics.swallowed("ios.ScrollViewHandler._apply")
|
|
1739
1806
|
|
|
1740
1807
|
def command(self, native_view: Any, name: str, args: Dict[str, Any]) -> Any:
|
|
1741
1808
|
if name == "scroll_to_offset":
|
|
@@ -1745,7 +1812,7 @@ class ScrollViewHandler(IOSViewHandler):
|
|
|
1745
1812
|
try:
|
|
1746
1813
|
native_view.setContentOffset_animated_((x, y), animated)
|
|
1747
1814
|
except Exception:
|
|
1748
|
-
|
|
1815
|
+
diagnostics.swallowed("ios.ScrollViewHandler.command")
|
|
1749
1816
|
return None
|
|
1750
1817
|
if name == "scroll_to_end":
|
|
1751
1818
|
animated = args.get("animated", True) is not False
|
|
@@ -1760,7 +1827,7 @@ class ScrollViewHandler(IOSViewHandler):
|
|
|
1760
1827
|
offset = (target_x, 0.0) if horizontal else (0.0, target_y)
|
|
1761
1828
|
native_view.setContentOffset_animated_(offset, animated)
|
|
1762
1829
|
except Exception:
|
|
1763
|
-
|
|
1830
|
+
diagnostics.swallowed("ios.ScrollViewHandler.command")
|
|
1764
1831
|
return None
|
|
1765
1832
|
if name == "get_scroll_offset":
|
|
1766
1833
|
try:
|
|
@@ -1796,10 +1863,11 @@ class ScrollViewHandler(IOSViewHandler):
|
|
|
1796
1863
|
if existing is not None:
|
|
1797
1864
|
existing.endRefreshing()
|
|
1798
1865
|
_pn_action_handlers.pop(_recognizer_ptr(existing), None)
|
|
1866
|
+
_pn_last_control_event_ts.pop(_recognizer_ptr(existing), None)
|
|
1799
1867
|
sv.setRefreshControl_(None)
|
|
1800
1868
|
sv.setAlwaysBounceVertical_(False)
|
|
1801
1869
|
except Exception:
|
|
1802
|
-
|
|
1870
|
+
diagnostics.swallowed("ios.ScrollViewHandler._apply_refresh")
|
|
1803
1871
|
return
|
|
1804
1872
|
try:
|
|
1805
1873
|
existing = sv.refreshControl
|
|
@@ -1816,7 +1884,7 @@ class ScrollViewHandler(IOSViewHandler):
|
|
|
1816
1884
|
try:
|
|
1817
1885
|
sv.setAlwaysBounceVertical_(True)
|
|
1818
1886
|
except Exception:
|
|
1819
|
-
|
|
1887
|
+
diagnostics.swallowed("ios.ScrollViewHandler._apply_refresh")
|
|
1820
1888
|
existing = rc
|
|
1821
1889
|
refreshing = bool(spec.get("refreshing")) if isinstance(spec, dict) else False
|
|
1822
1890
|
if refreshing:
|
|
@@ -1824,7 +1892,7 @@ class ScrollViewHandler(IOSViewHandler):
|
|
|
1824
1892
|
else:
|
|
1825
1893
|
existing.endRefreshing()
|
|
1826
1894
|
except Exception:
|
|
1827
|
-
|
|
1895
|
+
diagnostics.swallowed("ios.ScrollViewHandler._apply_refresh")
|
|
1828
1896
|
|
|
1829
1897
|
|
|
1830
1898
|
class ImageHandler(IOSViewHandler):
|
|
@@ -1842,12 +1910,12 @@ class ImageHandler(IOSViewHandler):
|
|
|
1842
1910
|
try:
|
|
1843
1911
|
iv.setTintColor_(_uicolor(props["tint_color"]))
|
|
1844
1912
|
except Exception:
|
|
1845
|
-
|
|
1913
|
+
diagnostics.swallowed("ios.ImageHandler._apply")
|
|
1846
1914
|
if "placeholder_color" in props and props["placeholder_color"] is not None:
|
|
1847
1915
|
try:
|
|
1848
1916
|
iv.setBackgroundColor_(_uicolor(props["placeholder_color"]))
|
|
1849
1917
|
except Exception:
|
|
1850
|
-
|
|
1918
|
+
diagnostics.swallowed("ios.ImageHandler._apply")
|
|
1851
1919
|
if "source" in props and props["source"]:
|
|
1852
1920
|
self._load_source(iv, str(props["source"]))
|
|
1853
1921
|
if "scale_type" in props and props["scale_type"]:
|
|
@@ -1873,7 +1941,7 @@ class ImageHandler(IOSViewHandler):
|
|
|
1873
1941
|
w, h = max_width, h * scale
|
|
1874
1942
|
return (w, h)
|
|
1875
1943
|
except Exception:
|
|
1876
|
-
|
|
1944
|
+
diagnostics.swallowed("ios.ImageHandler.measure_intrinsic")
|
|
1877
1945
|
return (0.0, 0.0)
|
|
1878
1946
|
|
|
1879
1947
|
def _load_source(self, iv: Any, source: str) -> None:
|
|
@@ -1894,7 +1962,7 @@ class ImageHandler(IOSViewHandler):
|
|
|
1894
1962
|
else:
|
|
1895
1963
|
_fire(iv, "on_error", f"image {source!r} not found")
|
|
1896
1964
|
except Exception:
|
|
1897
|
-
|
|
1965
|
+
diagnostics.swallowed("ios.ImageHandler._load_source")
|
|
1898
1966
|
|
|
1899
1967
|
def _load_data_uri(self, iv: Any, source: str) -> None:
|
|
1900
1968
|
"""Decode an inline ``data:`` URI (base64 payload) into the view."""
|
|
@@ -1942,7 +2010,7 @@ class ImageHandler(IOSViewHandler):
|
|
|
1942
2010
|
iv.setImage_(image)
|
|
1943
2011
|
_fire(iv, "on_load")
|
|
1944
2012
|
except Exception:
|
|
1945
|
-
|
|
2013
|
+
diagnostics.swallowed("ios.ImageHandler._load_async.on_ready")
|
|
1946
2014
|
|
|
1947
2015
|
def on_error(message: str) -> None:
|
|
1948
2016
|
if _state_of(iv).get("pending_uri") == source:
|
|
@@ -1950,7 +2018,7 @@ class ImageHandler(IOSViewHandler):
|
|
|
1950
2018
|
|
|
1951
2019
|
fetch(source, on_ready, on_error)
|
|
1952
2020
|
except Exception:
|
|
1953
|
-
|
|
2021
|
+
diagnostics.swallowed("ios.ImageHandler._load_async")
|
|
1954
2022
|
|
|
1955
2023
|
def _decode_downsampled(self, iv: Any, path: str) -> Any:
|
|
1956
2024
|
"""Decode a file, scaling down to the view's bounds if oversized.
|
|
@@ -2052,7 +2120,7 @@ def _tf_on_change_imp(self_ptr: int, _cmd: int, sender_ptr: int) -> None:
|
|
|
2052
2120
|
try:
|
|
2053
2121
|
view.setText_(text)
|
|
2054
2122
|
except Exception:
|
|
2055
|
-
|
|
2123
|
+
diagnostics.swallowed("ios._tf_on_change_imp")
|
|
2056
2124
|
finally:
|
|
2057
2125
|
state["suppress"] = False
|
|
2058
2126
|
_fire(view, "on_change", text)
|
|
@@ -2077,7 +2145,7 @@ def _tf_should_return_imp(self_ptr: int, _cmd: int, tf_ptr: int) -> bool:
|
|
|
2077
2145
|
_objc_msgSend.argtypes = [_ct.c_void_p, _ct.c_void_p]
|
|
2078
2146
|
_objc_msgSend(_ct.c_void_p(int(tf_ptr or 0)), _SEL_RESIGN_FIRST_RESPONDER)
|
|
2079
2147
|
except Exception:
|
|
2080
|
-
|
|
2148
|
+
diagnostics.swallowed("ios._tf_should_return_imp")
|
|
2081
2149
|
return True
|
|
2082
2150
|
|
|
2083
2151
|
|
|
@@ -2244,7 +2312,7 @@ class TextInputHandler(IOSViewHandler):
|
|
|
2244
2312
|
)
|
|
2245
2313
|
view.setAttributedPlaceholder_(attr)
|
|
2246
2314
|
except Exception:
|
|
2247
|
-
|
|
2315
|
+
diagnostics.swallowed("ios.TextInputHandler._apply")
|
|
2248
2316
|
if "font_size" in props and props["font_size"] is not None:
|
|
2249
2317
|
view.setFont_(UIFont.systemFontOfSize_(float(props["font_size"])))
|
|
2250
2318
|
if "color" in props and props["color"] is not None:
|
|
@@ -2255,7 +2323,7 @@ class TextInputHandler(IOSViewHandler):
|
|
|
2255
2323
|
try:
|
|
2256
2324
|
view.setSecureTextEntry_(bool(props["secure"]))
|
|
2257
2325
|
except Exception:
|
|
2258
|
-
|
|
2326
|
+
diagnostics.swallowed("ios.TextInputHandler._apply")
|
|
2259
2327
|
if "keyboard_type" in props and props["keyboard_type"] is not None:
|
|
2260
2328
|
mapping = {
|
|
2261
2329
|
"default": 0,
|
|
@@ -2270,19 +2338,19 @@ class TextInputHandler(IOSViewHandler):
|
|
|
2270
2338
|
try:
|
|
2271
2339
|
view.setKeyboardType_(mapping.get(props["keyboard_type"], 0))
|
|
2272
2340
|
except Exception:
|
|
2273
|
-
|
|
2341
|
+
diagnostics.swallowed("ios.TextInputHandler._apply")
|
|
2274
2342
|
if "auto_capitalize" in props and props["auto_capitalize"] is not None:
|
|
2275
2343
|
# UITextAutocapitalizationType: none=0, words=1, sentences=2, all=3.
|
|
2276
2344
|
mapping = {"none": 0, "words": 1, "sentences": 2, "characters": 3}
|
|
2277
2345
|
try:
|
|
2278
2346
|
view.setAutocapitalizationType_(mapping.get(props["auto_capitalize"], 2))
|
|
2279
2347
|
except Exception:
|
|
2280
|
-
|
|
2348
|
+
diagnostics.swallowed("ios.TextInputHandler._apply")
|
|
2281
2349
|
if "auto_correct" in props:
|
|
2282
2350
|
try:
|
|
2283
2351
|
view.setAutocorrectionType_(1 if props["auto_correct"] else 0)
|
|
2284
2352
|
except Exception:
|
|
2285
|
-
|
|
2353
|
+
diagnostics.swallowed("ios.TextInputHandler._apply")
|
|
2286
2354
|
if "return_key_type" in props and props["return_key_type"] is not None:
|
|
2287
2355
|
mapping = {
|
|
2288
2356
|
"default": 0,
|
|
@@ -2299,23 +2367,23 @@ class TextInputHandler(IOSViewHandler):
|
|
|
2299
2367
|
try:
|
|
2300
2368
|
view.setReturnKeyType_(mapping.get(props["return_key_type"], 0))
|
|
2301
2369
|
except Exception:
|
|
2302
|
-
|
|
2370
|
+
diagnostics.swallowed("ios.TextInputHandler._apply")
|
|
2303
2371
|
if "selection_color" in props and props["selection_color"] is not None:
|
|
2304
2372
|
try:
|
|
2305
2373
|
view.setTintColor_(_uicolor(props["selection_color"]))
|
|
2306
2374
|
except Exception:
|
|
2307
|
-
|
|
2375
|
+
diagnostics.swallowed("ios.TextInputHandler._apply")
|
|
2308
2376
|
if "text_content_type" in props:
|
|
2309
2377
|
tct = props["text_content_type"]
|
|
2310
2378
|
try:
|
|
2311
2379
|
view.setTextContentType_(_ui_text_content_type(str(tct)) if tct is not None else None)
|
|
2312
2380
|
except Exception:
|
|
2313
|
-
|
|
2381
|
+
diagnostics.swallowed("ios.TextInputHandler._apply")
|
|
2314
2382
|
if "clear_button" in props and is_field:
|
|
2315
2383
|
try:
|
|
2316
2384
|
view.setClearButtonMode_(1 if props["clear_button"] else 0) # 1 = WhileEditing
|
|
2317
2385
|
except Exception:
|
|
2318
|
-
|
|
2386
|
+
diagnostics.swallowed("ios.TextInputHandler._apply")
|
|
2319
2387
|
# ``editable`` is present only when False (read-only). A removed
|
|
2320
2388
|
# prop arrives as None on update, which means "editable again".
|
|
2321
2389
|
if "editable" in props:
|
|
@@ -2327,12 +2395,12 @@ class TextInputHandler(IOSViewHandler):
|
|
|
2327
2395
|
else:
|
|
2328
2396
|
view.setEditable_(resolved)
|
|
2329
2397
|
except Exception:
|
|
2330
|
-
|
|
2398
|
+
diagnostics.swallowed("ios.TextInputHandler._apply")
|
|
2331
2399
|
if "auto_focus" in props and props["auto_focus"]:
|
|
2332
2400
|
try:
|
|
2333
2401
|
view.becomeFirstResponder()
|
|
2334
2402
|
except Exception:
|
|
2335
|
-
|
|
2403
|
+
diagnostics.swallowed("ios.TextInputHandler._apply")
|
|
2336
2404
|
_apply_view_border(view, props)
|
|
2337
2405
|
_apply_shadow(view, props)
|
|
2338
2406
|
_apply_transform(view, props)
|
|
@@ -2341,7 +2409,7 @@ class TextInputHandler(IOSViewHandler):
|
|
|
2341
2409
|
try:
|
|
2342
2410
|
view.setAlpha_(float(props["opacity"]))
|
|
2343
2411
|
except Exception:
|
|
2344
|
-
|
|
2412
|
+
diagnostics.swallowed("ios.TextInputHandler._apply")
|
|
2345
2413
|
|
|
2346
2414
|
def measure_intrinsic(
|
|
2347
2415
|
self,
|
|
@@ -2357,13 +2425,13 @@ class TextInputHandler(IOSViewHandler):
|
|
|
2357
2425
|
try:
|
|
2358
2426
|
native_view.becomeFirstResponder()
|
|
2359
2427
|
except Exception:
|
|
2360
|
-
|
|
2428
|
+
diagnostics.swallowed("ios.TextInputHandler.command")
|
|
2361
2429
|
return None
|
|
2362
2430
|
if name == "blur":
|
|
2363
2431
|
try:
|
|
2364
2432
|
native_view.resignFirstResponder()
|
|
2365
2433
|
except Exception:
|
|
2366
|
-
|
|
2434
|
+
diagnostics.swallowed("ios.TextInputHandler.command")
|
|
2367
2435
|
return None
|
|
2368
2436
|
if name == "clear":
|
|
2369
2437
|
state = _state_of(native_view)
|
|
@@ -2481,7 +2549,7 @@ class ActivityIndicatorHandler(IOSViewHandler):
|
|
|
2481
2549
|
try:
|
|
2482
2550
|
ai.setActivityIndicatorViewStyle_(style)
|
|
2483
2551
|
except Exception:
|
|
2484
|
-
|
|
2552
|
+
diagnostics.swallowed("ios.ActivityIndicatorHandler._apply")
|
|
2485
2553
|
if "color" in props and props["color"] is not None:
|
|
2486
2554
|
ai.setColor_(_uicolor(props["color"]))
|
|
2487
2555
|
animating = props.get("animating")
|
|
@@ -2563,7 +2631,7 @@ class PressableHandler(IOSViewHandler):
|
|
|
2563
2631
|
try:
|
|
2564
2632
|
rec.setCancelsTouchesInView_(False)
|
|
2565
2633
|
except Exception:
|
|
2566
|
-
|
|
2634
|
+
diagnostics.swallowed("ios.PressableHandler._wire_press")
|
|
2567
2635
|
_set_recognizer_delegate(rec)
|
|
2568
2636
|
view.addGestureRecognizer_(rec)
|
|
2569
2637
|
rec.retain()
|
|
@@ -2643,7 +2711,7 @@ def _press_feedback(view: Any, pressed: bool) -> None:
|
|
|
2643
2711
|
UIView = ObjCClass("UIView")
|
|
2644
2712
|
UIView.animateWithDuration_animations_(duration, lambda: view.setAlpha_(opacity))
|
|
2645
2713
|
except Exception:
|
|
2646
|
-
|
|
2714
|
+
diagnostics.swallowed("ios._press_feedback")
|
|
2647
2715
|
|
|
2648
2716
|
|
|
2649
2717
|
# ======================================================================
|
|
@@ -2668,7 +2736,7 @@ class ProgressBarHandler(IOSViewHandler):
|
|
|
2668
2736
|
try:
|
|
2669
2737
|
ai.startAnimating()
|
|
2670
2738
|
except Exception:
|
|
2671
|
-
|
|
2739
|
+
diagnostics.swallowed("ios.ProgressBarHandler._build")
|
|
2672
2740
|
return ai
|
|
2673
2741
|
pv = ObjCClass("UIProgressView").alloc().init()
|
|
2674
2742
|
pv.setTranslatesAutoresizingMaskIntoConstraints_(True)
|
|
@@ -2684,27 +2752,27 @@ class ProgressBarHandler(IOSViewHandler):
|
|
|
2684
2752
|
try:
|
|
2685
2753
|
view.setColor_(_uicolor(props["color"]))
|
|
2686
2754
|
except Exception:
|
|
2687
|
-
|
|
2755
|
+
diagnostics.swallowed("ios.ProgressBarHandler._apply")
|
|
2688
2756
|
try:
|
|
2689
2757
|
view.startAnimating()
|
|
2690
2758
|
except Exception:
|
|
2691
|
-
|
|
2759
|
+
diagnostics.swallowed("ios.ProgressBarHandler._apply")
|
|
2692
2760
|
else:
|
|
2693
2761
|
if "value" in props and props["value"] is not None:
|
|
2694
2762
|
try:
|
|
2695
2763
|
view.setProgress_(float(props["value"]))
|
|
2696
2764
|
except Exception:
|
|
2697
|
-
|
|
2765
|
+
diagnostics.swallowed("ios.ProgressBarHandler._apply")
|
|
2698
2766
|
if "color" in props and props["color"] is not None:
|
|
2699
2767
|
try:
|
|
2700
2768
|
view.setProgressTintColor_(_uicolor(props["color"]))
|
|
2701
2769
|
except Exception:
|
|
2702
|
-
|
|
2770
|
+
diagnostics.swallowed("ios.ProgressBarHandler._apply")
|
|
2703
2771
|
if "track_color" in props and props["track_color"] is not None:
|
|
2704
2772
|
try:
|
|
2705
2773
|
view.setTrackTintColor_(_uicolor(props["track_color"]))
|
|
2706
2774
|
except Exception:
|
|
2707
|
-
|
|
2775
|
+
diagnostics.swallowed("ios.ProgressBarHandler._apply")
|
|
2708
2776
|
_apply_accessibility(view, props)
|
|
2709
2777
|
|
|
2710
2778
|
def measure_intrinsic(
|
|
@@ -2771,7 +2839,7 @@ def _webview_did_finish_imp(self_ptr: int, _cmd_ptr: int, _webview_ptr: int, _na
|
|
|
2771
2839
|
try:
|
|
2772
2840
|
wv.evaluateJavaScript_completionHandler_(str(js), None)
|
|
2773
2841
|
except Exception:
|
|
2774
|
-
|
|
2842
|
+
diagnostics.swallowed("ios._webview_did_finish_imp")
|
|
2775
2843
|
_fire(wv, "on_load", _webview_url(wv))
|
|
2776
2844
|
|
|
2777
2845
|
|
|
@@ -2855,7 +2923,7 @@ class WebViewHandler(IOSViewHandler):
|
|
|
2855
2923
|
try:
|
|
2856
2924
|
cls.declare_property(prop)
|
|
2857
2925
|
except Exception:
|
|
2858
|
-
|
|
2926
|
+
diagnostics.swallowed("ios.WebViewHandler._build")
|
|
2859
2927
|
_pn_wkwebview_declared = True
|
|
2860
2928
|
config = WKWebViewConfiguration.alloc().init()
|
|
2861
2929
|
delegate_ptr = _new_webview_delegate_ptr()
|
|
@@ -2867,14 +2935,14 @@ class WebViewHandler(IOSViewHandler):
|
|
|
2867
2935
|
try:
|
|
2868
2936
|
config.userContentController.addScriptMessageHandler_name_(delegate, "pythonnative")
|
|
2869
2937
|
except Exception:
|
|
2870
|
-
|
|
2938
|
+
diagnostics.swallowed("ios.WebViewHandler._build")
|
|
2871
2939
|
wv = WKWebView.alloc().initWithFrame_configuration_(((0, 0), (0, 0)), config)
|
|
2872
2940
|
wv.setTranslatesAutoresizingMaskIntoConstraints_(True)
|
|
2873
2941
|
if delegate is not None:
|
|
2874
2942
|
try:
|
|
2875
2943
|
wv.setNavigationDelegate_(delegate)
|
|
2876
2944
|
except Exception:
|
|
2877
|
-
|
|
2945
|
+
diagnostics.swallowed("ios.WebViewHandler._build")
|
|
2878
2946
|
if delegate_ptr:
|
|
2879
2947
|
_pn_webview_state[delegate_ptr] = {"view": wv, "inject_js": None}
|
|
2880
2948
|
self._delegate_ids[_objc_ptr(wv) or 0] = delegate_ptr
|
|
@@ -2907,7 +2975,7 @@ class WebViewHandler(IOSViewHandler):
|
|
|
2907
2975
|
try:
|
|
2908
2976
|
wv.loadHTMLString_baseURL_(str(props["html"]), None)
|
|
2909
2977
|
except Exception:
|
|
2910
|
-
|
|
2978
|
+
diagnostics.swallowed("ios.WebViewHandler._apply")
|
|
2911
2979
|
elif "url" in props and props["url"]:
|
|
2912
2980
|
try:
|
|
2913
2981
|
NSURL = ObjCClass("NSURL")
|
|
@@ -2915,38 +2983,38 @@ class WebViewHandler(IOSViewHandler):
|
|
|
2915
2983
|
url_obj = NSURL.URLWithString_(str(props["url"]))
|
|
2916
2984
|
wv.loadRequest_(NSURLRequest.requestWithURL_(url_obj))
|
|
2917
2985
|
except Exception:
|
|
2918
|
-
|
|
2986
|
+
diagnostics.swallowed("ios.WebViewHandler._apply")
|
|
2919
2987
|
if "scroll_enabled" in props:
|
|
2920
2988
|
enabled = props["scroll_enabled"]
|
|
2921
2989
|
try:
|
|
2922
2990
|
wv.scrollView.setScrollEnabled_(True if enabled is None else bool(enabled))
|
|
2923
2991
|
except Exception:
|
|
2924
|
-
|
|
2992
|
+
diagnostics.swallowed("ios.WebViewHandler._apply")
|
|
2925
2993
|
|
|
2926
2994
|
def command(self, native_view: Any, name: str, args: Dict[str, Any]) -> Any:
|
|
2927
2995
|
if name == "eval_js":
|
|
2928
2996
|
try:
|
|
2929
2997
|
native_view.evaluateJavaScript_completionHandler_(str(args.get("source", "")), None)
|
|
2930
2998
|
except Exception:
|
|
2931
|
-
|
|
2999
|
+
diagnostics.swallowed("ios.WebViewHandler.command")
|
|
2932
3000
|
return None
|
|
2933
3001
|
if name == "reload":
|
|
2934
3002
|
try:
|
|
2935
3003
|
native_view.reload()
|
|
2936
3004
|
except Exception:
|
|
2937
|
-
|
|
3005
|
+
diagnostics.swallowed("ios.WebViewHandler.command")
|
|
2938
3006
|
return None
|
|
2939
3007
|
if name == "go_back":
|
|
2940
3008
|
try:
|
|
2941
3009
|
native_view.goBack()
|
|
2942
3010
|
except Exception:
|
|
2943
|
-
|
|
3011
|
+
diagnostics.swallowed("ios.WebViewHandler.command")
|
|
2944
3012
|
return None
|
|
2945
3013
|
if name == "go_forward":
|
|
2946
3014
|
try:
|
|
2947
3015
|
native_view.goForward()
|
|
2948
3016
|
except Exception:
|
|
2949
|
-
|
|
3017
|
+
diagnostics.swallowed("ios.WebViewHandler.command")
|
|
2950
3018
|
return None
|
|
2951
3019
|
return None
|
|
2952
3020
|
|
|
@@ -3026,13 +3094,13 @@ class ModalHandler(IOSViewHandler):
|
|
|
3026
3094
|
try:
|
|
3027
3095
|
child.setTranslatesAutoresizingMaskIntoConstraints_(True)
|
|
3028
3096
|
except Exception:
|
|
3029
|
-
|
|
3097
|
+
diagnostics.swallowed("ios.ModalHandler.insert_child")
|
|
3030
3098
|
try:
|
|
3031
3099
|
content = modal["content_view"]
|
|
3032
3100
|
count = len(list(content.subviews or []))
|
|
3033
3101
|
content.insertSubview_atIndex_(child, max(0, min(index, count)))
|
|
3034
3102
|
except Exception:
|
|
3035
|
-
|
|
3103
|
+
diagnostics.swallowed("ios.ModalHandler.insert_child")
|
|
3036
3104
|
else:
|
|
3037
3105
|
buf = state.setdefault("pending_children", [])
|
|
3038
3106
|
buf.insert(max(0, min(index, len(buf))), child)
|
|
@@ -3041,7 +3109,7 @@ class ModalHandler(IOSViewHandler):
|
|
|
3041
3109
|
try:
|
|
3042
3110
|
child.removeFromSuperview()
|
|
3043
3111
|
except Exception:
|
|
3044
|
-
|
|
3112
|
+
diagnostics.swallowed("ios.ModalHandler.remove_child")
|
|
3045
3113
|
buf = _state_of(parent).get("pending_children")
|
|
3046
3114
|
if buf and child in buf:
|
|
3047
3115
|
buf.remove(child)
|
|
@@ -3082,7 +3150,7 @@ class ModalHandler(IOSViewHandler):
|
|
|
3082
3150
|
content.setFrame_(((0, 0), (bounds.size.width, bounds.size.height)))
|
|
3083
3151
|
content.setAutoresizingMask_(2 | 16) # FlexibleWidth | FlexibleHeight
|
|
3084
3152
|
except Exception:
|
|
3085
|
-
|
|
3153
|
+
diagnostics.swallowed("ios.ModalHandler._present")
|
|
3086
3154
|
|
|
3087
3155
|
# UIModalPresentationStyle: fullScreen=0, pageSheet=1,
|
|
3088
3156
|
# formSheet=2, overCurrentContext=6 (the dimmed overlay).
|
|
@@ -3091,7 +3159,7 @@ class ModalHandler(IOSViewHandler):
|
|
|
3091
3159
|
try:
|
|
3092
3160
|
controller.setModalPresentationStyle_(style_int)
|
|
3093
3161
|
except Exception:
|
|
3094
|
-
|
|
3162
|
+
diagnostics.swallowed("ios.ModalHandler._present")
|
|
3095
3163
|
# For sheet styles, ``dismiss_on_backdrop=False`` locks
|
|
3096
3164
|
# interactive (swipe / outside-tap) dismissal so the modal
|
|
3097
3165
|
# stays put until ``visible`` is driven back to False.
|
|
@@ -3099,7 +3167,7 @@ class ModalHandler(IOSViewHandler):
|
|
|
3099
3167
|
try:
|
|
3100
3168
|
controller.setModalInPresentation_(True)
|
|
3101
3169
|
except Exception:
|
|
3102
|
-
|
|
3170
|
+
diagnostics.swallowed("ios.ModalHandler._present")
|
|
3103
3171
|
|
|
3104
3172
|
def _on_present_complete() -> None:
|
|
3105
3173
|
_fire(placeholder, "on_show")
|
|
@@ -3115,7 +3183,7 @@ class ModalHandler(IOSViewHandler):
|
|
|
3115
3183
|
child.setTranslatesAutoresizingMaskIntoConstraints_(True)
|
|
3116
3184
|
content.addSubview_(child)
|
|
3117
3185
|
except Exception:
|
|
3118
|
-
|
|
3186
|
+
diagnostics.swallowed("ios.ModalHandler._present")
|
|
3119
3187
|
|
|
3120
3188
|
top = _top_view_controller_for_alert(UIApplication.sharedApplication)
|
|
3121
3189
|
if top is not None:
|
|
@@ -3133,7 +3201,7 @@ class ModalHandler(IOSViewHandler):
|
|
|
3133
3201
|
try:
|
|
3134
3202
|
controller.dismissViewControllerAnimated_completion_(True, None)
|
|
3135
3203
|
except Exception:
|
|
3136
|
-
|
|
3204
|
+
diagnostics.swallowed("ios.ModalHandler._dismiss")
|
|
3137
3205
|
_fire(placeholder, "on_dismiss")
|
|
3138
3206
|
|
|
3139
3207
|
|
|
@@ -3251,7 +3319,18 @@ class PortalHandler(IOSViewHandler):
|
|
|
3251
3319
|
return
|
|
3252
3320
|
current = overlay.superview
|
|
3253
3321
|
if current is not None and _objc_ptr(current) == _objc_ptr(host):
|
|
3254
|
-
host.
|
|
3322
|
+
# Already attached to the right host. Only reorder when
|
|
3323
|
+
# the overlay is not frontmost: reordering the subview
|
|
3324
|
+
# array during an active touch sequence (a re-render can
|
|
3325
|
+
# flush between touch-down and touch-up) can cancel the
|
|
3326
|
+
# touch and leave the portal's controls stuck
|
|
3327
|
+
# highlighted with their action never fired.
|
|
3328
|
+
try:
|
|
3329
|
+
front = host.subviews.lastObject()
|
|
3330
|
+
if front is None or _objc_ptr(front) != _objc_ptr(overlay):
|
|
3331
|
+
host.bringSubviewToFront_(overlay)
|
|
3332
|
+
except Exception:
|
|
3333
|
+
host.bringSubviewToFront_(overlay)
|
|
3255
3334
|
self._sync_overlay_frame(overlay, host)
|
|
3256
3335
|
return
|
|
3257
3336
|
if current is not None:
|
|
@@ -3259,7 +3338,7 @@ class PortalHandler(IOSViewHandler):
|
|
|
3259
3338
|
host.addSubview_(overlay)
|
|
3260
3339
|
self._sync_overlay_frame(overlay, host)
|
|
3261
3340
|
except Exception:
|
|
3262
|
-
|
|
3341
|
+
diagnostics.swallowed("ios.PortalHandler._ensure_attached")
|
|
3263
3342
|
|
|
3264
3343
|
@staticmethod
|
|
3265
3344
|
def _sync_overlay_frame(overlay: Any, host: Any) -> None:
|
|
@@ -3275,7 +3354,7 @@ class PortalHandler(IOSViewHandler):
|
|
|
3275
3354
|
overlay.setFrame_(((left, top), (w, h)))
|
|
3276
3355
|
overlay.setAutoresizingMask_(2 | 16) # FlexibleWidth | FlexibleHeight
|
|
3277
3356
|
except Exception:
|
|
3278
|
-
|
|
3357
|
+
diagnostics.swallowed("ios.PortalHandler._sync_overlay_frame")
|
|
3279
3358
|
|
|
3280
3359
|
def insert_child(self, parent: Any, child: Any, index: int) -> None:
|
|
3281
3360
|
self._ensure_attached(parent)
|
|
@@ -3302,7 +3381,7 @@ class PortalHandler(IOSViewHandler):
|
|
|
3302
3381
|
try:
|
|
3303
3382
|
overlay.release()
|
|
3304
3383
|
except Exception:
|
|
3305
|
-
|
|
3384
|
+
diagnostics.swallowed("ios.PortalHandler._teardown")
|
|
3306
3385
|
|
|
3307
3386
|
|
|
3308
3387
|
# ======================================================================
|
|
@@ -3336,7 +3415,7 @@ class StatusBarHandler(IOSViewHandler):
|
|
|
3336
3415
|
mapping = {"default": 3, "light": 1, "dark": 3}
|
|
3337
3416
|
app.setStatusBarStyle_animated_(mapping.get(props["bar_style"], 0), True)
|
|
3338
3417
|
except Exception:
|
|
3339
|
-
|
|
3418
|
+
diagnostics.swallowed("ios.StatusBarHandler._apply")
|
|
3340
3419
|
|
|
3341
3420
|
def set_frame(self, native_view: Any, x: float, y: float, width: float, height: float) -> None:
|
|
3342
3421
|
return
|
|
@@ -3402,7 +3481,7 @@ def _ensure_keyboard_observer() -> None:
|
|
|
3402
3481
|
None,
|
|
3403
3482
|
)
|
|
3404
3483
|
except Exception:
|
|
3405
|
-
|
|
3484
|
+
diagnostics.swallowed("ios._ensure_keyboard_observer")
|
|
3406
3485
|
|
|
3407
3486
|
|
|
3408
3487
|
class KeyboardAvoidingViewHandler(IOSViewHandler):
|
|
@@ -3446,7 +3525,7 @@ def _tabbar_did_select_imp(self_ptr: int, cmd_ptr: int, tabbar_ptr: int, item_pt
|
|
|
3446
3525
|
if 0 <= index < len(items):
|
|
3447
3526
|
_fire_ptr(int(tabbar_ptr), "on_tab_select", items[index].get("name", ""))
|
|
3448
3527
|
except Exception:
|
|
3449
|
-
|
|
3528
|
+
diagnostics.swallowed("ios._tabbar_did_select_imp")
|
|
3450
3529
|
|
|
3451
3530
|
|
|
3452
3531
|
_tabbar_imp_ref = _DELEGATE_IMP_TYPE(_tabbar_did_select_imp)
|
|
@@ -3532,7 +3611,7 @@ class TabBarHandler(IOSViewHandler):
|
|
|
3532
3611
|
try:
|
|
3533
3612
|
tab_bar.setItems_animated_(bar_items, False)
|
|
3534
3613
|
except Exception:
|
|
3535
|
-
|
|
3614
|
+
diagnostics.swallowed("ios.TabBarHandler._set_bar_items")
|
|
3536
3615
|
|
|
3537
3616
|
def _resolve_icon(self, UIImage: Any, icon: Any) -> Any:
|
|
3538
3617
|
"""Resolve a tab icon spec to a UIImage, or return None.
|
|
@@ -3566,7 +3645,7 @@ class TabBarHandler(IOSViewHandler):
|
|
|
3566
3645
|
if i < len(all_items):
|
|
3567
3646
|
tab_bar.setSelectedItem_(all_items[i])
|
|
3568
3647
|
except Exception:
|
|
3569
|
-
|
|
3648
|
+
diagnostics.swallowed("ios.TabBarHandler._set_active")
|
|
3570
3649
|
break
|
|
3571
3650
|
|
|
3572
3651
|
|
|
@@ -3589,7 +3668,7 @@ def _top_view_controller_for_alert(app: Any) -> Any:
|
|
|
3589
3668
|
try:
|
|
3590
3669
|
window = app.keyWindow
|
|
3591
3670
|
except Exception:
|
|
3592
|
-
|
|
3671
|
+
diagnostics.swallowed("ios._top_view_controller_for_alert")
|
|
3593
3672
|
|
|
3594
3673
|
if window is None:
|
|
3595
3674
|
try:
|
|
@@ -3601,7 +3680,7 @@ def _top_view_controller_for_alert(app: Any) -> Any:
|
|
|
3601
3680
|
if window is None and windows:
|
|
3602
3681
|
window = windows[0]
|
|
3603
3682
|
except Exception:
|
|
3604
|
-
|
|
3683
|
+
diagnostics.swallowed("ios._top_view_controller_for_alert")
|
|
3605
3684
|
|
|
3606
3685
|
if window is None:
|
|
3607
3686
|
return None
|
|
@@ -3618,7 +3697,7 @@ def _top_view_controller_for_alert(app: Any) -> Any:
|
|
|
3618
3697
|
if visible is not None:
|
|
3619
3698
|
top = visible
|
|
3620
3699
|
except Exception:
|
|
3621
|
-
|
|
3700
|
+
diagnostics.swallowed("ios._top_view_controller_for_alert")
|
|
3622
3701
|
|
|
3623
3702
|
while top is not None:
|
|
3624
3703
|
try:
|
|
@@ -3666,7 +3745,7 @@ def _present_alert(
|
|
|
3666
3745
|
try:
|
|
3667
3746
|
on_result(index)
|
|
3668
3747
|
except Exception:
|
|
3669
|
-
|
|
3748
|
+
diagnostics.swallowed("ios._present_alert._deliver")
|
|
3670
3749
|
|
|
3671
3750
|
def _present_on_main() -> None:
|
|
3672
3751
|
try:
|
|
@@ -3767,7 +3846,7 @@ class PickerHandler(IOSViewHandler):
|
|
|
3767
3846
|
try:
|
|
3768
3847
|
btn.setTitle_forState_(_picker_button_title(merged), 0)
|
|
3769
3848
|
except Exception:
|
|
3770
|
-
|
|
3849
|
+
diagnostics.swallowed("ios.PickerHandler._apply")
|
|
3771
3850
|
_apply_accessibility(btn, props)
|
|
3772
3851
|
|
|
3773
3852
|
def measure_intrinsic(self, native_view: Any, max_width: float, max_height: float) -> Tuple[float, float]:
|
|
@@ -3803,10 +3882,10 @@ def _checkbox_set_image(btn: Any) -> None:
|
|
|
3803
3882
|
if tinted is not None:
|
|
3804
3883
|
image = tinted
|
|
3805
3884
|
except Exception:
|
|
3806
|
-
|
|
3885
|
+
diagnostics.swallowed("ios._checkbox_set_image")
|
|
3807
3886
|
btn.setImage_forState_(image, 0)
|
|
3808
3887
|
except Exception:
|
|
3809
|
-
|
|
3888
|
+
diagnostics.swallowed("ios._checkbox_set_image")
|
|
3810
3889
|
|
|
3811
3890
|
|
|
3812
3891
|
def _checkbox_toggle(btn: Any) -> None:
|
|
@@ -3844,7 +3923,7 @@ class CheckboxHandler(IOSViewHandler):
|
|
|
3844
3923
|
btn.setTitleColor_forState_(_uicolor("#111111"), 0)
|
|
3845
3924
|
btn.setTintColor_(_uicolor("#111111"))
|
|
3846
3925
|
except Exception:
|
|
3847
|
-
|
|
3926
|
+
diagnostics.swallowed("ios.CheckboxHandler._apply")
|
|
3848
3927
|
if "value" in props:
|
|
3849
3928
|
state["value"] = bool(props["value"])
|
|
3850
3929
|
if "label" in props:
|
|
@@ -3852,14 +3931,14 @@ class CheckboxHandler(IOSViewHandler):
|
|
|
3852
3931
|
try:
|
|
3853
3932
|
btn.setTitle_forState_(str(label) if label is not None else "", 0)
|
|
3854
3933
|
except Exception:
|
|
3855
|
-
|
|
3934
|
+
diagnostics.swallowed("ios.CheckboxHandler._apply")
|
|
3856
3935
|
# An image-bearing custom button is not exposed to the
|
|
3857
3936
|
# accessibility tree by title alone; mirror the label
|
|
3858
3937
|
# explicitly (an accessibility_label prop still wins below).
|
|
3859
3938
|
try:
|
|
3860
3939
|
btn.setAccessibilityLabel_(str(label) if label is not None else "")
|
|
3861
3940
|
except Exception:
|
|
3862
|
-
|
|
3941
|
+
diagnostics.swallowed("ios.CheckboxHandler._apply")
|
|
3863
3942
|
if "disabled" in props:
|
|
3864
3943
|
# ``disabled`` is present only when True; a removed prop
|
|
3865
3944
|
# (None) re-enables the control.
|
|
@@ -3867,7 +3946,7 @@ class CheckboxHandler(IOSViewHandler):
|
|
|
3867
3946
|
try:
|
|
3868
3947
|
btn.setEnabled_(not disabled)
|
|
3869
3948
|
except Exception:
|
|
3870
|
-
|
|
3949
|
+
diagnostics.swallowed("ios.CheckboxHandler._apply")
|
|
3871
3950
|
_checkbox_set_image(btn)
|
|
3872
3951
|
_apply_accessibility(btn, props)
|
|
3873
3952
|
|
|
@@ -3924,7 +4003,7 @@ class SegmentedControlHandler(IOSViewHandler):
|
|
|
3924
4003
|
for i, title in enumerate(new_segments):
|
|
3925
4004
|
control.insertSegmentWithTitle_atIndex_animated_(title, i, False)
|
|
3926
4005
|
except Exception:
|
|
3927
|
-
|
|
4006
|
+
diagnostics.swallowed("ios.SegmentedControlHandler._apply")
|
|
3928
4007
|
finally:
|
|
3929
4008
|
state["suppress"] = False
|
|
3930
4009
|
rebuilt = True
|
|
@@ -3937,7 +4016,7 @@ class SegmentedControlHandler(IOSViewHandler):
|
|
|
3937
4016
|
try:
|
|
3938
4017
|
control.setSelectedSegmentIndex_(int(merged.get("selected_index", 0) or 0))
|
|
3939
4018
|
except Exception:
|
|
3940
|
-
|
|
4019
|
+
diagnostics.swallowed("ios.SegmentedControlHandler._apply")
|
|
3941
4020
|
finally:
|
|
3942
4021
|
state["suppress"] = False
|
|
3943
4022
|
if "tint_color" in props and props["tint_color"] is not None:
|
|
@@ -3945,11 +4024,11 @@ class SegmentedControlHandler(IOSViewHandler):
|
|
|
3945
4024
|
try:
|
|
3946
4025
|
control.setSelectedSegmentTintColor_(color) # iOS 13+
|
|
3947
4026
|
except Exception:
|
|
3948
|
-
|
|
4027
|
+
diagnostics.swallowed("ios.SegmentedControlHandler._apply")
|
|
3949
4028
|
try:
|
|
3950
4029
|
control.setTintColor_(color)
|
|
3951
4030
|
except Exception:
|
|
3952
|
-
|
|
4031
|
+
diagnostics.swallowed("ios.SegmentedControlHandler._apply")
|
|
3953
4032
|
if "enabled" in props:
|
|
3954
4033
|
# ``enabled`` is present only when False; a removed prop
|
|
3955
4034
|
# (None) re-enables the control.
|
|
@@ -3957,7 +4036,7 @@ class SegmentedControlHandler(IOSViewHandler):
|
|
|
3957
4036
|
try:
|
|
3958
4037
|
control.setEnabled_(True if enabled is None else bool(enabled))
|
|
3959
4038
|
except Exception:
|
|
3960
|
-
|
|
4039
|
+
diagnostics.swallowed("ios.SegmentedControlHandler._apply")
|
|
3961
4040
|
_apply_accessibility(control, props)
|
|
3962
4041
|
|
|
3963
4042
|
def measure_intrinsic(self, native_view: Any, max_width: float, max_height: float) -> Tuple[float, float]:
|
|
@@ -3996,7 +4075,7 @@ def _date_formatter(mode: str) -> Any:
|
|
|
3996
4075
|
NSLocale = ObjCClass("NSLocale")
|
|
3997
4076
|
formatter.setLocale_(NSLocale.alloc().initWithLocaleIdentifier_("en_US_POSIX"))
|
|
3998
4077
|
except Exception:
|
|
3999
|
-
|
|
4078
|
+
diagnostics.swallowed("ios._date_formatter")
|
|
4000
4079
|
formatter.retain()
|
|
4001
4080
|
_pn_date_formatters[fmt] = formatter
|
|
4002
4081
|
return formatter
|
|
@@ -4013,7 +4092,7 @@ class DatePickerHandler(IOSViewHandler):
|
|
|
4013
4092
|
try:
|
|
4014
4093
|
picker.setPreferredDatePickerStyle_(2) # UIDatePickerStyleCompact
|
|
4015
4094
|
except Exception:
|
|
4016
|
-
|
|
4095
|
+
diagnostics.swallowed("ios.DatePickerHandler._build")
|
|
4017
4096
|
|
|
4018
4097
|
def _on_change() -> None:
|
|
4019
4098
|
state = _state_of(picker)
|
|
@@ -4038,7 +4117,7 @@ class DatePickerHandler(IOSViewHandler):
|
|
|
4038
4117
|
try:
|
|
4039
4118
|
picker.setDatePickerMode_(mode_map.get(mode, 1))
|
|
4040
4119
|
except Exception:
|
|
4041
|
-
|
|
4120
|
+
diagnostics.swallowed("ios.DatePickerHandler._apply")
|
|
4042
4121
|
if "minimum" in props:
|
|
4043
4122
|
self._set_bound(picker, "setMinimumDate_", props["minimum"], mode)
|
|
4044
4123
|
if "maximum" in props:
|
|
@@ -4053,7 +4132,7 @@ class DatePickerHandler(IOSViewHandler):
|
|
|
4053
4132
|
try:
|
|
4054
4133
|
picker.setDate_animated_(date, False)
|
|
4055
4134
|
except Exception:
|
|
4056
|
-
|
|
4135
|
+
diagnostics.swallowed("ios.DatePickerHandler._apply")
|
|
4057
4136
|
finally:
|
|
4058
4137
|
state["suppress"] = False
|
|
4059
4138
|
if "enabled" in props:
|
|
@@ -4063,7 +4142,7 @@ class DatePickerHandler(IOSViewHandler):
|
|
|
4063
4142
|
try:
|
|
4064
4143
|
picker.setEnabled_(True if enabled is None else bool(enabled))
|
|
4065
4144
|
except Exception:
|
|
4066
|
-
|
|
4145
|
+
diagnostics.swallowed("ios.DatePickerHandler._apply")
|
|
4067
4146
|
_apply_accessibility(picker, props)
|
|
4068
4147
|
|
|
4069
4148
|
def _set_bound(self, picker: Any, selector: str, value: Any, mode: str) -> None:
|
|
@@ -4074,7 +4153,7 @@ class DatePickerHandler(IOSViewHandler):
|
|
|
4074
4153
|
date = _date_formatter(mode).dateFromString_(str(value))
|
|
4075
4154
|
getattr(picker, selector)(date)
|
|
4076
4155
|
except Exception:
|
|
4077
|
-
|
|
4156
|
+
diagnostics.swallowed("ios.DatePickerHandler._set_bound")
|
|
4078
4157
|
|
|
4079
4158
|
def measure_intrinsic(self, native_view: Any, max_width: float, max_height: float) -> Tuple[float, float]:
|
|
4080
4159
|
try:
|
|
@@ -4219,7 +4298,7 @@ def _table_cell_imp(self_ptr: int, cmd_ptr: int, tv_ptr: int, ip_ptr: int) -> in
|
|
|
4219
4298
|
cell.setFrame_(((0, 0), (cell_w, row_h)))
|
|
4220
4299
|
cell.contentView.setFrame_(((0, 0), (cell_w, row_h)))
|
|
4221
4300
|
except Exception:
|
|
4222
|
-
|
|
4301
|
+
diagnostics.swallowed("ios._table_cell_imp")
|
|
4223
4302
|
|
|
4224
4303
|
content = cell.contentView
|
|
4225
4304
|
try:
|
|
@@ -4229,7 +4308,7 @@ def _table_cell_imp(self_ptr: int, cmd_ptr: int, tv_ptr: int, ip_ptr: int) -> in
|
|
|
4229
4308
|
for sub in list(existing_subs or []):
|
|
4230
4309
|
sub.removeFromSuperview()
|
|
4231
4310
|
except Exception:
|
|
4232
|
-
|
|
4311
|
+
diagnostics.swallowed("ios._table_cell_imp")
|
|
4233
4312
|
|
|
4234
4313
|
# Only rows in (or near) the visible content window get a
|
|
4235
4314
|
# mounted subtree. UITableView's accessibility container calls
|
|
@@ -4259,7 +4338,7 @@ def _table_cell_imp(self_ptr: int, cmd_ptr: int, tv_ptr: int, ip_ptr: int) -> in
|
|
|
4259
4338
|
try:
|
|
4260
4339
|
cell.setAccessibilityElementsHidden_(not in_window)
|
|
4261
4340
|
except Exception:
|
|
4262
|
-
|
|
4341
|
+
diagnostics.swallowed("ios._table_cell_imp")
|
|
4263
4342
|
|
|
4264
4343
|
if in_window and info is not None:
|
|
4265
4344
|
render_row = info.get("render_row")
|
|
@@ -4278,7 +4357,7 @@ def _table_cell_imp(self_ptr: int, cmd_ptr: int, tv_ptr: int, ip_ptr: int) -> in
|
|
|
4278
4357
|
try:
|
|
4279
4358
|
root.setFrame_(((0, 0), (cell_w, row_h)))
|
|
4280
4359
|
except Exception:
|
|
4281
|
-
|
|
4360
|
+
diagnostics.swallowed("ios._table_cell_imp")
|
|
4282
4361
|
except Exception:
|
|
4283
4362
|
print(f"[VirtualList][iOS] mount for row={row} raised:")
|
|
4284
4363
|
_tb.print_exc()
|
|
@@ -4303,7 +4382,7 @@ def _table_did_select_imp(self_ptr: int, cmd_ptr: int, tv_ptr: int, ip_ptr: int)
|
|
|
4303
4382
|
_objc_msgSend.argtypes = [_ct.c_void_p, _ct.c_void_p, _ct.c_void_p, _ct.c_bool]
|
|
4304
4383
|
_objc_msgSend(_ct.c_void_p(tv_ptr), _SEL_DESELECT_ROW, _ct.c_void_p(ip_ptr), True)
|
|
4305
4384
|
except Exception:
|
|
4306
|
-
|
|
4385
|
+
diagnostics.swallowed("ios._table_did_select_imp")
|
|
4307
4386
|
_fire_ptr(int(tv_ptr or 0), "on_row_press", row)
|
|
4308
4387
|
|
|
4309
4388
|
|
|
@@ -4429,7 +4508,7 @@ class VirtualListHandler(IOSViewHandler):
|
|
|
4429
4508
|
estimate = float(props.get("row_height", 44.0) or 44.0)
|
|
4430
4509
|
tv.setEstimatedRowHeight_(max(1.0, estimate))
|
|
4431
4510
|
except Exception:
|
|
4432
|
-
|
|
4511
|
+
diagnostics.swallowed("ios.VirtualListHandler._build")
|
|
4433
4512
|
tv.retain()
|
|
4434
4513
|
_pn_retained_views.append(tv)
|
|
4435
4514
|
|
|
@@ -4487,12 +4566,12 @@ class VirtualListHandler(IOSViewHandler):
|
|
|
4487
4566
|
try:
|
|
4488
4567
|
tv.setShowsVerticalScrollIndicator_(visible)
|
|
4489
4568
|
except Exception:
|
|
4490
|
-
|
|
4569
|
+
diagnostics.swallowed("ios.VirtualListHandler._apply")
|
|
4491
4570
|
if data_changed and not initial:
|
|
4492
4571
|
try:
|
|
4493
4572
|
tv.reloadData()
|
|
4494
4573
|
except Exception:
|
|
4495
|
-
|
|
4574
|
+
diagnostics.swallowed("ios.VirtualListHandler._apply")
|
|
4496
4575
|
|
|
4497
4576
|
def _teardown(self, tv: Any) -> None:
|
|
4498
4577
|
info = self._source_state(tv)
|
|
@@ -4501,7 +4580,7 @@ class VirtualListHandler(IOSViewHandler):
|
|
|
4501
4580
|
try:
|
|
4502
4581
|
info["pool"].release_all()
|
|
4503
4582
|
except Exception:
|
|
4504
|
-
|
|
4583
|
+
diagnostics.swallowed("ios.VirtualListHandler._teardown")
|
|
4505
4584
|
info["tv"] = None
|
|
4506
4585
|
_pn_table_state.pop(info.get("source_ptr"), None)
|
|
4507
4586
|
|
|
@@ -4525,7 +4604,7 @@ class VirtualListHandler(IOSViewHandler):
|
|
|
4525
4604
|
try:
|
|
4526
4605
|
native_view.setContentOffset_animated_((0.0, y), animated)
|
|
4527
4606
|
except Exception:
|
|
4528
|
-
|
|
4607
|
+
diagnostics.swallowed("ios.VirtualListHandler.command")
|
|
4529
4608
|
return None
|
|
4530
4609
|
if name == "scroll_to_index":
|
|
4531
4610
|
index = int(args.get("index", 0) or 0)
|
|
@@ -4538,7 +4617,7 @@ class VirtualListHandler(IOSViewHandler):
|
|
|
4538
4617
|
# 1 = UITableViewScrollPositionTop
|
|
4539
4618
|
native_view.scrollToRowAtIndexPath_atScrollPosition_animated_(path, 1, animated)
|
|
4540
4619
|
except Exception:
|
|
4541
|
-
|
|
4620
|
+
diagnostics.swallowed("ios.VirtualListHandler.command")
|
|
4542
4621
|
return None
|
|
4543
4622
|
if name == "scroll_to_end":
|
|
4544
4623
|
animated = args.get("animated", True) is not False
|
|
@@ -4548,7 +4627,7 @@ class VirtualListHandler(IOSViewHandler):
|
|
|
4548
4627
|
target = max(0.0, content_h - bounds_h)
|
|
4549
4628
|
native_view.setContentOffset_animated_((0.0, target), animated)
|
|
4550
4629
|
except Exception:
|
|
4551
|
-
|
|
4630
|
+
diagnostics.swallowed("ios.VirtualListHandler.command")
|
|
4552
4631
|
return None
|
|
4553
4632
|
if name == "get_scroll_offset":
|
|
4554
4633
|
try:
|