ruflet 0.0.7 → 0.0.8

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.
Files changed (238) hide show
  1. checksums.yaml +4 -4
  2. data/lib/ruflet/version.rb +1 -1
  3. data/lib/ruflet_ui/ruflet/app.rb +10 -14
  4. data/lib/ruflet_ui/ruflet/control.rb +120 -7
  5. data/lib/ruflet_ui/ruflet/dsl.rb +68 -4
  6. data/lib/ruflet_ui/ruflet/event.rb +15 -1
  7. data/lib/ruflet_ui/ruflet/events/gesture_events.rb +552 -0
  8. data/lib/ruflet_ui/ruflet/page.rb +558 -77
  9. data/lib/ruflet_ui/ruflet/types/geometry.rb +65 -0
  10. data/lib/ruflet_ui/ruflet/types/text_style.rb +191 -0
  11. data/lib/ruflet_ui/ruflet/ui/control_factory.rb +35 -5
  12. data/lib/ruflet_ui/ruflet/ui/control_methods.rb +1 -0
  13. data/lib/ruflet_ui/ruflet/ui/control_registry.rb +22 -2
  14. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinoactionsheet_control.rb +57 -0
  15. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinoactionsheetaction_control.rb +58 -0
  16. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinoactivityindicator_control.rb +57 -0
  17. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinoalertdialog_control.rb +39 -0
  18. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinoappbar_control.rb +44 -0
  19. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinobottomsheet_control.rb +38 -0
  20. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinobutton_control.rb +73 -0
  21. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinocheckbox_control.rb +70 -0
  22. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinocontextmenu_control.rb +34 -0
  23. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinocontextmenuaction_control.rb +36 -0
  24. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinodatepicker_control.rb +67 -0
  25. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinodialogaction_control.rb +35 -0
  26. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinofilledbutton_control.rb +73 -0
  27. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinolisttile_control.rb +67 -0
  28. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinonavigationbar_control.rb +61 -0
  29. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinopicker_control.rb +66 -0
  30. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinoradio_control.rb +66 -0
  31. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinosegmentedbutton_control.rb +63 -0
  32. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinoslider_control.rb +64 -0
  33. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinoslidingsegmentedbutton_control.rb +60 -0
  34. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinoswitch_control.rb +73 -0
  35. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinotextfield_control.rb +159 -0
  36. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinotimerpicker_control.rb +61 -0
  37. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinotintedbutton_control.rb +73 -0
  38. data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/ruflet_controls.rb +89 -0
  39. data/lib/ruflet_ui/ruflet/ui/controls/materials/alertdialog_control.rb +58 -0
  40. data/lib/ruflet_ui/ruflet/ui/controls/materials/appbar_control.rb +53 -0
  41. data/lib/ruflet_ui/ruflet/ui/controls/materials/audio_control.rb +36 -0
  42. data/lib/ruflet_ui/ruflet/ui/controls/materials/autocomplete_control.rb +58 -0
  43. data/lib/ruflet_ui/ruflet/ui/controls/materials/badge_control.rb +31 -0
  44. data/lib/ruflet_ui/ruflet/ui/controls/materials/banner_control.rb +47 -0
  45. data/lib/ruflet_ui/ruflet/ui/controls/materials/bottomappbar_control.rb +62 -0
  46. data/lib/ruflet_ui/ruflet/ui/controls/materials/bottomsheet_control.rb +48 -0
  47. data/lib/ruflet_ui/ruflet/ui/controls/materials/button_control.rb +69 -0
  48. data/lib/ruflet_ui/ruflet/ui/controls/materials/card_control.rb +63 -0
  49. data/lib/ruflet_ui/ruflet/ui/controls/materials/chart_controls.rb +321 -0
  50. data/lib/ruflet_ui/ruflet/ui/controls/materials/checkbox_control.rb +76 -0
  51. data/lib/ruflet_ui/ruflet/ui/controls/materials/chip_control.rb +88 -0
  52. data/lib/ruflet_ui/ruflet/ui/controls/materials/circleavatar_control.rb +62 -0
  53. data/lib/ruflet_ui/ruflet/ui/controls/materials/container_control.rb +81 -0
  54. data/lib/ruflet_ui/ruflet/ui/controls/materials/contextmenu_control.rb +63 -0
  55. data/lib/ruflet_ui/ruflet/ui/controls/materials/datacell_control.rb +38 -0
  56. data/lib/ruflet_ui/ruflet/ui/controls/materials/datacolumn_control.rb +34 -0
  57. data/lib/ruflet_ui/ruflet/ui/controls/materials/datarow_control.rb +35 -0
  58. data/lib/ruflet_ui/ruflet/ui/controls/materials/datatable_control.rb +78 -0
  59. data/lib/ruflet_ui/ruflet/ui/controls/materials/datepicker_control.rb +55 -0
  60. data/lib/ruflet_ui/ruflet/ui/controls/materials/daterangepicker_control.rb +57 -0
  61. data/lib/ruflet_ui/ruflet/ui/controls/materials/divider_control.rb +36 -0
  62. data/lib/ruflet_ui/ruflet/ui/controls/materials/dropdown_control.rb +99 -0
  63. data/lib/ruflet_ui/ruflet/ui/controls/materials/dropdownm2_control.rb +119 -0
  64. data/lib/ruflet_ui/ruflet/ui/controls/materials/dropdownoption_control.rb +35 -0
  65. data/lib/ruflet_ui/ruflet/ui/controls/materials/expansionpanel_control.rb +61 -0
  66. data/lib/ruflet_ui/ruflet/ui/controls/materials/expansionpanellist_control.rb +60 -0
  67. data/lib/ruflet_ui/ruflet/ui/controls/materials/expansiontile_control.rb +82 -0
  68. data/lib/ruflet_ui/ruflet/ui/controls/materials/filledbutton_control.rb +69 -0
  69. data/lib/ruflet_ui/ruflet/ui/controls/materials/fillediconbutton_control.rb +81 -0
  70. data/lib/ruflet_ui/ruflet/ui/controls/materials/filledtonalbutton_control.rb +69 -0
  71. data/lib/ruflet_ui/ruflet/ui/controls/materials/filledtonaliconbutton_control.rb +81 -0
  72. data/lib/ruflet_ui/ruflet/ui/controls/materials/floatingactionbutton_control.rb +73 -0
  73. data/lib/ruflet_ui/ruflet/ui/controls/materials/iconbutton_control.rb +81 -0
  74. data/lib/ruflet_ui/ruflet/ui/controls/materials/listtile_control.rb +87 -0
  75. data/lib/ruflet_ui/ruflet/ui/controls/materials/menubar_control.rb +33 -0
  76. data/lib/ruflet_ui/ruflet/ui/controls/materials/menuitembutton_control.rb +67 -0
  77. data/lib/ruflet_ui/ruflet/ui/controls/materials/navigationbar_control.rb +67 -0
  78. data/lib/ruflet_ui/ruflet/ui/controls/materials/navigationbardestination_control.rb +35 -0
  79. data/lib/ruflet_ui/ruflet/ui/controls/materials/navigationdrawer_control.rb +41 -0
  80. data/lib/ruflet_ui/ruflet/ui/controls/materials/navigationdrawerdestination_control.rb +34 -0
  81. data/lib/ruflet_ui/ruflet/ui/controls/materials/navigationrail_control.rb +70 -0
  82. data/lib/ruflet_ui/ruflet/ui/controls/materials/navigationraildestination_control.rb +36 -0
  83. data/lib/ruflet_ui/ruflet/ui/controls/materials/option_control.rb +35 -0
  84. data/lib/ruflet_ui/ruflet/ui/controls/materials/outlinedbutton_control.rb +66 -0
  85. data/lib/ruflet_ui/ruflet/ui/controls/materials/outlinediconbutton_control.rb +81 -0
  86. data/lib/ruflet_ui/ruflet/ui/controls/materials/popupmenubutton_control.rb +74 -0
  87. data/lib/ruflet_ui/ruflet/ui/controls/materials/popupmenuitem_control.rb +38 -0
  88. data/lib/ruflet_ui/ruflet/ui/controls/materials/progressbar_control.rb +64 -0
  89. data/lib/ruflet_ui/ruflet/ui/controls/materials/progressring_control.rb +65 -0
  90. data/lib/ruflet_ui/ruflet/ui/controls/materials/radio_control.rb +70 -0
  91. data/lib/ruflet_ui/ruflet/ui/controls/materials/radiogroup_control.rb +33 -0
  92. data/lib/ruflet_ui/ruflet/ui/controls/materials/rangeslider_control.rb +67 -0
  93. data/lib/ruflet_ui/ruflet/ui/controls/materials/reorderablelistview_control.rb +80 -0
  94. data/lib/ruflet_ui/ruflet/ui/controls/materials/ruflet_controls.rb +252 -0
  95. data/lib/ruflet_ui/ruflet/ui/controls/materials/searchbar_control.rb +94 -0
  96. data/lib/ruflet_ui/ruflet/ui/controls/materials/segment_control.rb +33 -0
  97. data/lib/ruflet_ui/ruflet/ui/controls/materials/segmentedbutton_control.rb +63 -0
  98. data/lib/ruflet_ui/ruflet/ui/controls/materials/selectionarea_control.rb +32 -0
  99. data/lib/ruflet_ui/ruflet/ui/controls/materials/slider_control.rb +76 -0
  100. data/lib/ruflet_ui/ruflet/ui/controls/materials/snackbar_control.rb +51 -0
  101. data/lib/ruflet_ui/ruflet/ui/controls/materials/submenubutton_control.rb +66 -0
  102. data/lib/ruflet_ui/ruflet/ui/controls/materials/switch_control.rb +77 -0
  103. data/lib/ruflet_ui/ruflet/ui/controls/materials/tab_control.rb +35 -0
  104. data/lib/ruflet_ui/ruflet/ui/controls/materials/tabbar_control.rb +77 -0
  105. data/lib/ruflet_ui/ruflet/ui/controls/materials/tabbarview_control.rb +57 -0
  106. data/lib/ruflet_ui/ruflet/ui/controls/materials/tabs_control.rb +59 -0
  107. data/lib/ruflet_ui/ruflet/ui/controls/materials/textbutton_control.rb +66 -0
  108. data/lib/ruflet_ui/ruflet/ui/controls/materials/textfield_control.rb +148 -0
  109. data/lib/ruflet_ui/ruflet/ui/controls/materials/timepicker_control.rb +50 -0
  110. data/lib/ruflet_ui/ruflet/ui/controls/materials/verticaldivider_control.rb +36 -0
  111. data/lib/ruflet_ui/ruflet/ui/controls/materials/video_control.rb +39 -0
  112. data/lib/ruflet_ui/ruflet/ui/controls/materials/webview_control.rb +35 -0
  113. data/lib/ruflet_ui/ruflet/ui/controls/ruflet_controls.rb +447 -0
  114. data/lib/ruflet_ui/ruflet/ui/controls/shared/animatedswitcher_control.rb +59 -0
  115. data/lib/ruflet_ui/ruflet/ui/controls/shared/arc_control.rb +29 -0
  116. data/lib/ruflet_ui/ruflet/ui/controls/shared/autofillgroup_control.rb +32 -0
  117. data/lib/ruflet_ui/ruflet/ui/controls/shared/basepage_control.rb +44 -0
  118. data/lib/ruflet_ui/ruflet/ui/controls/shared/browsercontextmenu_control.rb +21 -0
  119. data/lib/ruflet_ui/ruflet/ui/controls/shared/canvas_control.rb +57 -0
  120. data/lib/ruflet_ui/ruflet/ui/controls/shared/circle_control.rb +25 -0
  121. data/lib/ruflet_ui/ruflet/ui/controls/shared/color_control.rb +23 -0
  122. data/lib/ruflet_ui/ruflet/ui/controls/shared/column_control.rb +67 -0
  123. data/lib/ruflet_ui/ruflet/ui/controls/shared/dialogs_control.rb +22 -0
  124. data/lib/ruflet_ui/ruflet/ui/controls/shared/dismissible_control.rb +66 -0
  125. data/lib/ruflet_ui/ruflet/ui/controls/shared/draggable_control.rb +39 -0
  126. data/lib/ruflet_ui/ruflet/ui/controls/shared/dragtarget_control.rb +36 -0
  127. data/lib/ruflet_ui/ruflet/ui/controls/shared/fill_control.rb +22 -0
  128. data/lib/ruflet_ui/ruflet/ui/controls/shared/fletapp_control.rb +62 -0
  129. data/lib/ruflet_ui/ruflet/ui/controls/shared/gesturedetector_control.rb +129 -0
  130. data/lib/ruflet_ui/ruflet/ui/controls/shared/gridview_control.rb +71 -0
  131. data/lib/ruflet_ui/ruflet/ui/controls/shared/hero_control.rb +56 -0
  132. data/lib/ruflet_ui/ruflet/ui/controls/shared/icon_control.rb +64 -0
  133. data/lib/ruflet_ui/ruflet/ui/controls/shared/image_control.rb +27 -0
  134. data/lib/ruflet_ui/ruflet/ui/controls/shared/interactiveviewer_control.rb +69 -0
  135. data/lib/ruflet_ui/ruflet/ui/controls/shared/keyboardlistener_control.rb +36 -0
  136. data/lib/ruflet_ui/ruflet/ui/controls/shared/line_control.rb +26 -0
  137. data/lib/ruflet_ui/ruflet/ui/controls/shared/listview_control.rb +71 -0
  138. data/lib/ruflet_ui/ruflet/ui/controls/shared/markdown_control.rb +70 -0
  139. data/lib/ruflet_ui/ruflet/ui/controls/shared/mergesemantics_control.rb +31 -0
  140. data/lib/ruflet_ui/ruflet/ui/controls/shared/oval_control.rb +26 -0
  141. data/lib/ruflet_ui/ruflet/ui/controls/shared/overlay_control.rb +22 -0
  142. data/lib/ruflet_ui/ruflet/ui/controls/shared/page_control.rb +74 -0
  143. data/lib/ruflet_ui/ruflet/ui/controls/shared/pagelet_control.rb +64 -0
  144. data/lib/ruflet_ui/ruflet/ui/controls/shared/pageview_control.rb +64 -0
  145. data/lib/ruflet_ui/ruflet/ui/controls/shared/path_control.rb +23 -0
  146. data/lib/ruflet_ui/ruflet/ui/controls/shared/placeholder_control.rb +58 -0
  147. data/lib/ruflet_ui/ruflet/ui/controls/shared/points_control.rb +24 -0
  148. data/lib/ruflet_ui/ruflet/ui/controls/shared/rect_control.rb +27 -0
  149. data/lib/ruflet_ui/ruflet/ui/controls/shared/reorderabledraghandle_control.rb +56 -0
  150. data/lib/ruflet_ui/ruflet/ui/controls/shared/responsiverow_control.rb +61 -0
  151. data/lib/ruflet_ui/ruflet/ui/controls/shared/row_control.rb +67 -0
  152. data/lib/ruflet_ui/ruflet/ui/controls/shared/ruflet_controls.rb +140 -0
  153. data/lib/ruflet_ui/ruflet/ui/controls/shared/safearea_control.rb +61 -0
  154. data/lib/ruflet_ui/ruflet/ui/controls/shared/semantics_control.rb +79 -0
  155. data/lib/ruflet_ui/ruflet/ui/controls/shared/serviceregistry_control.rb +21 -0
  156. data/lib/ruflet_ui/ruflet/ui/controls/shared/shadermask_control.rb +57 -0
  157. data/lib/ruflet_ui/ruflet/ui/controls/shared/shadow_control.rb +25 -0
  158. data/lib/ruflet_ui/ruflet/ui/controls/shared/shimmer_control.rb +60 -0
  159. data/lib/ruflet_ui/ruflet/ui/controls/shared/stack_control.rb +58 -0
  160. data/lib/ruflet_ui/ruflet/ui/controls/shared/text_control.rb +32 -0
  161. data/lib/ruflet_ui/ruflet/ui/controls/shared/textspan_control.rb +39 -0
  162. data/lib/ruflet_ui/ruflet/ui/controls/shared/transparentpointer_control.rb +54 -0
  163. data/lib/ruflet_ui/ruflet/ui/controls/shared/view_control.rb +77 -0
  164. data/lib/ruflet_ui/ruflet/ui/controls/shared/window_control.rb +56 -0
  165. data/lib/ruflet_ui/ruflet/ui/controls/shared/windowdragarea_control.rb +58 -0
  166. data/lib/ruflet_ui/ruflet/ui/cupertino_control_factory.rb +3 -30
  167. data/lib/ruflet_ui/ruflet/ui/material_control_factory.rb +3 -87
  168. data/lib/ruflet_ui/ruflet/ui/material_control_methods.rb +44 -2
  169. data/lib/ruflet_ui/ruflet/ui/material_control_registry.rb +26 -12
  170. data/lib/ruflet_ui/ruflet/ui/services/ruflet/accelerometer_control.rb +26 -0
  171. data/lib/ruflet_ui/ruflet/ui/services/ruflet/barometer_control.rb +26 -0
  172. data/lib/ruflet_ui/ruflet/ui/services/ruflet/battery_control.rb +22 -0
  173. data/lib/ruflet_ui/ruflet/ui/services/ruflet/camera_control.rb +58 -0
  174. data/lib/ruflet_ui/ruflet/ui/services/ruflet/clipboard_control.rb +21 -0
  175. data/lib/ruflet_ui/ruflet/ui/services/ruflet/connectivity_control.rb +22 -0
  176. data/lib/ruflet_ui/ruflet/ui/services/ruflet/filepicker_control.rb +23 -0
  177. data/lib/ruflet_ui/ruflet/ui/services/ruflet/flashlight_control.rb +22 -0
  178. data/lib/ruflet_ui/ruflet/ui/services/ruflet/gyroscope_control.rb +26 -0
  179. data/lib/ruflet_ui/ruflet/ui/services/ruflet/hapticfeedback_control.rb +21 -0
  180. data/lib/ruflet_ui/ruflet/ui/services/ruflet/magnetometer_control.rb +26 -0
  181. data/lib/ruflet_ui/ruflet/ui/services/ruflet/screenbrightness_control.rb +23 -0
  182. data/lib/ruflet_ui/ruflet/ui/services/ruflet/screenshot_control.rb +31 -0
  183. data/lib/ruflet_ui/ruflet/ui/services/ruflet/semanticsservice_control.rb +21 -0
  184. data/lib/ruflet_ui/ruflet/ui/services/ruflet/shakedetector_control.rb +26 -0
  185. data/lib/ruflet_ui/ruflet/ui/services/ruflet/share_control.rb +21 -0
  186. data/lib/ruflet_ui/ruflet/ui/services/ruflet/sharedpreferences_control.rb +21 -0
  187. data/lib/ruflet_ui/ruflet/ui/services/ruflet/storagepaths_control.rb +21 -0
  188. data/lib/ruflet_ui/ruflet/ui/services/ruflet/urllauncher_control.rb +21 -0
  189. data/lib/ruflet_ui/ruflet/ui/services/ruflet/useraccelerometer_control.rb +26 -0
  190. data/lib/ruflet_ui/ruflet/ui/services/ruflet/wakelock_control.rb +21 -0
  191. data/lib/ruflet_ui/ruflet/ui/services/ruflet_services.rb +66 -0
  192. data/lib/ruflet_ui/ruflet/ui/shared_control_forwarders.rb +41 -1
  193. data/lib/ruflet_ui/ruflet/ui/widget_builder.rb +9 -0
  194. data/lib/ruflet_ui.rb +14 -1
  195. metadata +178 -44
  196. data/lib/ruflet/manifest_compiler.rb +0 -62
  197. data/lib/ruflet_ui/ruflet/ui/controls/cupertino/cupertino_action_sheet_control.rb +0 -13
  198. data/lib/ruflet_ui/ruflet/ui/controls/cupertino/cupertino_alert_dialog_control.rb +0 -13
  199. data/lib/ruflet_ui/ruflet/ui/controls/cupertino/cupertino_button_control.rb +0 -13
  200. data/lib/ruflet_ui/ruflet/ui/controls/cupertino/cupertino_dialog_action_control.rb +0 -24
  201. data/lib/ruflet_ui/ruflet/ui/controls/cupertino/cupertino_filled_button_control.rb +0 -13
  202. data/lib/ruflet_ui/ruflet/ui/controls/cupertino/cupertino_navigation_bar_control.rb +0 -13
  203. data/lib/ruflet_ui/ruflet/ui/controls/cupertino/cupertino_slider_control.rb +0 -13
  204. data/lib/ruflet_ui/ruflet/ui/controls/cupertino/cupertino_switch_control.rb +0 -13
  205. data/lib/ruflet_ui/ruflet/ui/controls/cupertino/cupertino_text_field_control.rb +0 -13
  206. data/lib/ruflet_ui/ruflet/ui/controls/material/alert_dialog_control.rb +0 -13
  207. data/lib/ruflet_ui/ruflet/ui/controls/material/app_bar_control.rb +0 -13
  208. data/lib/ruflet_ui/ruflet/ui/controls/material/bottom_sheet_control.rb +0 -13
  209. data/lib/ruflet_ui/ruflet/ui/controls/material/button_control.rb +0 -24
  210. data/lib/ruflet_ui/ruflet/ui/controls/material/checkbox_control.rb +0 -13
  211. data/lib/ruflet_ui/ruflet/ui/controls/material/column_control.rb +0 -13
  212. data/lib/ruflet_ui/ruflet/ui/controls/material/container_control.rb +0 -13
  213. data/lib/ruflet_ui/ruflet/ui/controls/material/drag_target_control.rb +0 -13
  214. data/lib/ruflet_ui/ruflet/ui/controls/material/draggable_control.rb +0 -13
  215. data/lib/ruflet_ui/ruflet/ui/controls/material/elevated_button_control.rb +0 -13
  216. data/lib/ruflet_ui/ruflet/ui/controls/material/filled_button_control.rb +0 -13
  217. data/lib/ruflet_ui/ruflet/ui/controls/material/floating_action_button_control.rb +0 -28
  218. data/lib/ruflet_ui/ruflet/ui/controls/material/gesture_detector_control.rb +0 -13
  219. data/lib/ruflet_ui/ruflet/ui/controls/material/grid_view_control.rb +0 -13
  220. data/lib/ruflet_ui/ruflet/ui/controls/material/icon_button_control.rb +0 -13
  221. data/lib/ruflet_ui/ruflet/ui/controls/material/icon_control.rb +0 -24
  222. data/lib/ruflet_ui/ruflet/ui/controls/material/image_control.rb +0 -13
  223. data/lib/ruflet_ui/ruflet/ui/controls/material/markdown_control.rb +0 -13
  224. data/lib/ruflet_ui/ruflet/ui/controls/material/navigation_bar_control.rb +0 -13
  225. data/lib/ruflet_ui/ruflet/ui/controls/material/navigation_bar_destination_control.rb +0 -13
  226. data/lib/ruflet_ui/ruflet/ui/controls/material/radio_control.rb +0 -13
  227. data/lib/ruflet_ui/ruflet/ui/controls/material/radio_group_control.rb +0 -13
  228. data/lib/ruflet_ui/ruflet/ui/controls/material/row_control.rb +0 -13
  229. data/lib/ruflet_ui/ruflet/ui/controls/material/snack_bar_control.rb +0 -68
  230. data/lib/ruflet_ui/ruflet/ui/controls/material/stack_control.rb +0 -13
  231. data/lib/ruflet_ui/ruflet/ui/controls/material/tab_bar_control.rb +0 -13
  232. data/lib/ruflet_ui/ruflet/ui/controls/material/tab_bar_view_control.rb +0 -13
  233. data/lib/ruflet_ui/ruflet/ui/controls/material/tab_control.rb +0 -13
  234. data/lib/ruflet_ui/ruflet/ui/controls/material/tabs_control.rb +0 -63
  235. data/lib/ruflet_ui/ruflet/ui/controls/material/text_button_control.rb +0 -13
  236. data/lib/ruflet_ui/ruflet/ui/controls/material/text_control.rb +0 -13
  237. data/lib/ruflet_ui/ruflet/ui/controls/material/text_field_control.rb +0 -13
  238. data/lib/ruflet_ui/ruflet/ui/controls/material/view_control.rb +0 -13
@@ -4,6 +4,7 @@ require_relative "event"
4
4
  require "ruflet_protocol"
5
5
  require_relative "control"
6
6
  require_relative "ui/widget_builder"
7
+ require_relative "ui/control_factory"
7
8
  require_relative "icons/material_icon_lookup"
8
9
  require_relative "icons/cupertino_icon_lookup"
9
10
  require "set"
@@ -15,19 +16,11 @@ module Ruflet
15
16
  class Page
16
17
  PAGE_PROP_KEYS = %w[route title vertical_alignment horizontal_alignment scroll].freeze
17
18
  DIALOG_PROP_KEYS = %w[dialog snack_bar bottom_sheet].freeze
18
- BUTTON_TEXT_TYPES = %w[button elevatedbutton textbutton filledbutton].freeze
19
- DEPRECATED_PAGE_WIDGET_METHODS = %i[
20
- control widget view column center row stack container gesture_detector gesturedetector draggable
21
- drag_target dragtarget text button elevated_button elevatedbutton text_button textbutton filled_button
22
- filledbutton icon_button iconbutton text_field textfield checkbox radio radio_group radiogroup
23
- alert_dialog alertdialog markdown icon image app_bar appbar floating_action_button snack_bar snackbar
24
- bottom_sheet bottomsheet tabs tab tab_bar tabbar tab_bar_view tabbarview navigation_bar navigationbar
25
- navigation_bar_destination navigationbardestination fab cupertino_button
26
- cupertinobutton cupertino_filled_button cupertinofilledbutton cupertino_text_field cupertinotextfield
27
- cupertino_switch cupertinoswitch cupertino_slider cupertinoslider cupertino_alert_dialog
28
- cupertinoalertdialog cupertino_action_sheet cupertinoactionsheet cupertino_dialog_action
29
- cupertinodialogaction cupertino_navigation_bar cupertinonavigationbar
30
- ].freeze
19
+ WIDGET_HELPER_METHODS = (
20
+ Ruflet::UI::MaterialControlMethods.instance_methods(false) +
21
+ Ruflet::UI::CupertinoControlMethods.instance_methods(false) +
22
+ %i[control widget]
23
+ ).map(&:to_s).to_set.freeze
31
24
 
32
25
  attr_reader :session_id, :client_details, :views
33
26
 
@@ -62,6 +55,7 @@ module Ruflet
62
55
  controls: []
63
56
  )
64
57
  @invoke_waiters = {}
58
+ @invoke_callbacks = {}
65
59
  @invoke_waiters_mutex = Mutex.new
66
60
  refresh_overlay_container!
67
61
  refresh_services_container!
@@ -161,6 +155,45 @@ module Ruflet
161
155
  self
162
156
  end
163
157
 
158
+ def remove_service(*value)
159
+ targets = value.flatten.compact
160
+ return self if targets.empty?
161
+
162
+ @services_container.props["_services"] = services.reject do |service|
163
+ targets.any? do |target|
164
+ case target
165
+ when Control
166
+ service.equal?(target) || (!target.id.nil? && service.id.to_s == target.id.to_s)
167
+ else
168
+ needle = target.to_s
169
+ service.id.to_s == needle || service.type.to_s.downcase == needle.downcase
170
+ end
171
+ end
172
+ end
173
+
174
+ refresh_services_container!
175
+ push_services_update!
176
+ self
177
+ end
178
+
179
+ def service(type, **props)
180
+ mapped_props = normalize_props(props || {})
181
+ id = mapped_props.delete("id")
182
+ normalized_type = type.to_s.downcase
183
+
184
+ existing =
185
+ if id
186
+ services.find { |s| s.is_a?(Control) && s.id.to_s == id.to_s }
187
+ else
188
+ services.find { |s| s.is_a?(Control) && s.type.to_s.downcase == normalized_type }
189
+ end
190
+ return existing if existing
191
+
192
+ svc = Ruflet::UI::ControlFactory.build(type.to_s, id: id&.to_s, **mapped_props)
193
+ add_service(svc) unless services.include?(svc)
194
+ svc
195
+ end
196
+
164
197
  def go(route, **query_params)
165
198
  @page_props["route"] = build_route(route, query_params)
166
199
  dispatch_page_event(name: "route_change", data: @page_props["route"])
@@ -233,80 +266,327 @@ module Ruflet
233
266
  self
234
267
  end
235
268
 
236
- def invoke(control_or_id, method_name, args: nil, timeout: 10)
237
- control = resolve_control(control_or_id)
238
- return nil unless control
269
+ def invoke(control_or_id, method_name, args: nil, timeout: 10, on_result: nil)
270
+ control_id =
271
+ if page_control_target?(control_or_id)
272
+ 1
273
+ else
274
+ control = resolve_control(control_or_id)
275
+ return nil unless control
276
+ control.wire_id
277
+ end
239
278
 
240
279
  call_id = "call_#{Ruflet::Control.generate_id}"
241
- send_message(Protocol::ACTIONS[:invoke_control_method], {
242
- "control_id" => control.wire_id,
280
+ if on_result.respond_to?(:call)
281
+ @invoke_waiters_mutex.synchronize { @invoke_callbacks[call_id] = on_result }
282
+ unless timeout.nil?
283
+ Thread.new(call_id, timeout.to_f) do |pending_call_id, invoke_timeout|
284
+ sleep([invoke_timeout, 0.0].max + 0.1)
285
+ callback = @invoke_waiters_mutex.synchronize { @invoke_callbacks.delete(pending_call_id) }
286
+ callback&.call(nil, "execution expired")
287
+ rescue StandardError => e
288
+ Kernel.warn("invoke timeout callback error: #{e.class}: #{e.message}")
289
+ end
290
+ end
291
+ end
292
+ payload = {
293
+ "control_id" => control_id,
243
294
  "call_id" => call_id,
244
295
  "name" => method_name.to_s,
245
- "args" => args,
246
- "timeout" => timeout
247
- })
296
+ "args" => args
297
+ }
298
+ payload["timeout"] = timeout unless timeout.nil?
299
+ send_message(Protocol::ACTIONS[:invoke_control_method], payload)
248
300
 
249
301
  call_id
250
302
  end
251
303
 
252
- def launch_url(url, mode: "external_application", web_view_configuration: nil, browser_configuration: nil, web_only_window_name: nil, timeout: 10)
253
- launcher = ensure_url_launcher_service
304
+ # Synchronous invoke for controls/services that must return a value
305
+ # before continuing (e.g. picker selection, camera discovery/init).
306
+ def invoke_sync(control_or_id, method_name, args: nil, timeout: 10)
307
+ invoke_and_wait(control_or_id, method_name, args: args, timeout: timeout)
308
+ end
309
+
310
+ def launch_url(url, mode: nil, web_view_configuration: nil, browser_configuration: nil, web_only_window_name: nil, timeout: 10, on_result: nil)
311
+ url_launcher = ensure_url_launcher_service
312
+ args = { "url" => url }
313
+ args["mode"] = mode unless mode.nil?
314
+ args["web_view_configuration"] = web_view_configuration unless web_view_configuration.nil?
315
+ args["browser_configuration"] = browser_configuration unless browser_configuration.nil?
316
+ args["web_only_window_name"] = web_only_window_name unless web_only_window_name.nil?
254
317
  invoke(
255
- launcher,
318
+ url_launcher,
256
319
  "launch_url",
257
- args: {
258
- "url" => url,
259
- "mode" => mode,
260
- "web_view_configuration" => web_view_configuration,
261
- "browser_configuration" => browser_configuration,
262
- "web_only_window_name" => web_only_window_name
263
- }.compact,
264
- timeout: timeout
320
+ args: args,
321
+ timeout: timeout,
322
+ on_result: on_result
265
323
  )
266
324
  end
267
325
 
268
326
  def can_launch_url(url, timeout: 10)
269
- launcher = ensure_url_launcher_service
270
- invoke(launcher, "can_launch_url", args: { "url" => url }, timeout: timeout)
327
+ url_launcher = ensure_url_launcher_service
328
+ invoke(url_launcher, "can_launch_url", args: { "url" => url }, timeout: timeout)
329
+ end
330
+
331
+ # File picker helpers: create an ephemeral service, invoke method, and dispose it.
332
+ def pick_files(
333
+ dialog_title: nil,
334
+ initial_directory: nil,
335
+ file_type: "any",
336
+ allowed_extensions: nil,
337
+ allow_multiple: false,
338
+ with_data: false,
339
+ timeout: nil,
340
+ on_result: nil
341
+ )
342
+ invoke_file_picker(
343
+ "pick_files",
344
+ {
345
+ "dialog_title" => dialog_title,
346
+ "initial_directory" => initial_directory,
347
+ "file_type" => file_type,
348
+ "allowed_extensions" => allowed_extensions,
349
+ "allow_multiple" => allow_multiple,
350
+ "with_data" => with_data
351
+ },
352
+ timeout: timeout,
353
+ on_result: on_result
354
+ )
355
+ end
356
+
357
+ def save_file(
358
+ dialog_title: nil,
359
+ file_name: nil,
360
+ initial_directory: nil,
361
+ file_type: "any",
362
+ allowed_extensions: nil,
363
+ src_bytes: nil,
364
+ timeout: nil,
365
+ on_result: nil
366
+ )
367
+ invoke_file_picker(
368
+ "save_file",
369
+ {
370
+ "dialog_title" => dialog_title,
371
+ "file_name" => file_name,
372
+ "initial_directory" => initial_directory,
373
+ "file_type" => file_type,
374
+ "allowed_extensions" => allowed_extensions,
375
+ "src_bytes" => src_bytes
376
+ },
377
+ timeout: timeout,
378
+ on_result: on_result
379
+ )
380
+ end
381
+
382
+ def get_directory_path(dialog_title: nil, initial_directory: nil, timeout: nil, on_result: nil)
383
+ invoke_file_picker(
384
+ "get_directory_path",
385
+ {
386
+ "dialog_title" => dialog_title,
387
+ "initial_directory" => initial_directory
388
+ },
389
+ timeout: timeout,
390
+ on_result: on_result
391
+ )
392
+ end
393
+
394
+ def set_clipboard(value, timeout: nil, on_result: nil)
395
+ invoke_clipboard_method(
396
+ "set_data",
397
+ args: { "data" => value.to_s },
398
+ timeout: timeout,
399
+ on_result: on_result
400
+ )
401
+ end
402
+
403
+ def get_clipboard(timeout: nil, on_result: nil)
404
+ invoke_clipboard_method("get_data", timeout: timeout, on_result: on_result)
405
+ end
406
+
407
+ def set_clipboard_files(files, timeout: nil, on_result: nil)
408
+ invoke_clipboard_method(
409
+ "set_files",
410
+ args: { "files" => Array(files).map(&:to_s) },
411
+ timeout: timeout,
412
+ on_result: on_result
413
+ )
414
+ end
415
+
416
+ def get_clipboard_files(timeout: nil, on_result: nil)
417
+ invoke_clipboard_method("get_files", timeout: timeout, on_result: on_result)
418
+ end
419
+
420
+ def set_clipboard_image(value, timeout: nil, on_result: nil)
421
+ invoke_clipboard_method(
422
+ "set_image",
423
+ args: { "data" => value },
424
+ timeout: timeout,
425
+ on_result: on_result
426
+ )
427
+ end
428
+
429
+ def get_clipboard_image(timeout: nil, on_result: nil)
430
+ invoke_clipboard_method("get_image", timeout: timeout, on_result: on_result)
431
+ end
432
+
433
+ def get_connectivity(timeout: nil, on_result: nil)
434
+ invoke_connectivity_method("get_connectivity", timeout: timeout, on_result: on_result)
435
+ end
436
+
437
+ def get_battery_level(timeout: nil, on_result: nil)
438
+ invoke_battery_method("get_battery_level", timeout: timeout, on_result: on_result)
439
+ end
440
+
441
+ def get_battery_state(timeout: nil, on_result: nil)
442
+ invoke_battery_method("get_battery_state", timeout: timeout, on_result: on_result)
443
+ end
444
+
445
+ def battery_save_mode?(timeout: nil, on_result: nil)
446
+ invoke_battery_method("is_in_battery_save_mode", timeout: timeout, on_result: on_result)
447
+ end
448
+
449
+ def get_application_cache_directory(timeout: nil, on_result: nil)
450
+ invoke_storage_paths("get_application_cache_directory", timeout: timeout, on_result: on_result)
451
+ end
452
+
453
+ def get_application_documents_directory(timeout: nil, on_result: nil)
454
+ invoke_storage_paths("get_application_documents_directory", timeout: timeout, on_result: on_result)
455
+ end
456
+
457
+ def get_application_support_directory(timeout: nil, on_result: nil)
458
+ invoke_storage_paths("get_application_support_directory", timeout: timeout, on_result: on_result)
459
+ end
460
+
461
+ def get_downloads_directory(timeout: nil, on_result: nil)
462
+ invoke_storage_paths("get_downloads_directory", timeout: timeout, on_result: on_result)
463
+ end
464
+
465
+ def get_external_cache_directories(timeout: nil, on_result: nil)
466
+ invoke_storage_paths("get_external_cache_directories", timeout: timeout, on_result: on_result)
271
467
  end
272
468
 
273
- def set_clipboard(value, timeout: 10)
274
- clipboard = ensure_clipboard_service
275
- invoke(clipboard, "set", args: { "data" => value.to_s }, timeout: timeout)
469
+ def get_external_storage_directories(timeout: nil, on_result: nil)
470
+ invoke_storage_paths("get_external_storage_directories", timeout: timeout, on_result: on_result)
276
471
  end
277
472
 
278
- def get_clipboard(timeout: 10)
279
- clipboard = ensure_clipboard_service
280
- invoke(clipboard, "get", timeout: timeout)
473
+ def get_library_directory(timeout: nil, on_result: nil)
474
+ invoke_storage_paths("get_library_directory", timeout: timeout, on_result: on_result)
281
475
  end
282
476
 
283
- def set_clipboard_files(files, timeout: 10)
284
- clipboard = ensure_clipboard_service
285
- invoke(clipboard, "set_files", args: { "files" => Array(files).map(&:to_s) }, timeout: timeout)
477
+ def get_external_storage_directory(timeout: nil, on_result: nil)
478
+ invoke_storage_paths("get_external_storage_directory", timeout: timeout, on_result: on_result)
286
479
  end
287
480
 
288
- def get_clipboard_files(timeout: 10)
289
- clipboard = ensure_clipboard_service
290
- invoke(clipboard, "get_files", timeout: timeout)
481
+ def get_temporary_directory(timeout: nil, on_result: nil)
482
+ invoke_storage_paths("get_temporary_directory", timeout: timeout, on_result: on_result)
291
483
  end
292
484
 
293
- def set_clipboard_image(value, timeout: 10)
294
- clipboard = ensure_clipboard_service
295
- invoke(clipboard, "set_image", args: { "data" => value }, timeout: timeout)
485
+ def get_console_log_filename(timeout: nil, on_result: nil)
486
+ invoke_storage_paths("get_console_log_filename", timeout: timeout, on_result: on_result)
296
487
  end
297
488
 
298
- def get_clipboard_image(timeout: 10)
299
- clipboard = ensure_clipboard_service
300
- invoke(clipboard, "get_image", timeout: timeout)
489
+ def share_text(
490
+ text:,
491
+ title: nil,
492
+ subject: nil,
493
+ preview_thumbnail: nil,
494
+ share_position_origin: nil,
495
+ download_fallback_enabled: true,
496
+ mail_to_fallback_enabled: true,
497
+ excluded_cupertino_activities: nil,
498
+ timeout: nil,
499
+ on_result: nil
500
+ )
501
+ share = ensure_share_service
502
+ invoke(
503
+ share,
504
+ "share_text",
505
+ args: {
506
+ "text" => text,
507
+ "title" => title,
508
+ "subject" => subject,
509
+ "preview_thumbnail" => preview_thumbnail,
510
+ "share_position_origin" => share_position_origin,
511
+ "download_fallback_enabled" => download_fallback_enabled,
512
+ "mail_to_fallback_enabled" => mail_to_fallback_enabled,
513
+ "excluded_cupertino_activities" => excluded_cupertino_activities
514
+ },
515
+ timeout: timeout,
516
+ on_result: on_result
517
+ )
518
+ end
519
+
520
+ def share_uri(
521
+ uri:,
522
+ share_position_origin: nil,
523
+ excluded_cupertino_activities: nil,
524
+ timeout: nil,
525
+ on_result: nil
526
+ )
527
+ share = ensure_share_service
528
+ invoke(
529
+ share,
530
+ "share_uri",
531
+ args: {
532
+ "uri" => uri,
533
+ "share_position_origin" => share_position_origin,
534
+ "excluded_cupertino_activities" => excluded_cupertino_activities
535
+ },
536
+ timeout: timeout,
537
+ on_result: on_result
538
+ )
539
+ end
540
+
541
+ def share_files(
542
+ files:,
543
+ text: nil,
544
+ title: nil,
545
+ subject: nil,
546
+ preview_thumbnail: nil,
547
+ share_position_origin: nil,
548
+ download_fallback_enabled: true,
549
+ mail_to_fallback_enabled: true,
550
+ excluded_cupertino_activities: nil,
551
+ timeout: nil,
552
+ on_result: nil
553
+ )
554
+ share = ensure_share_service
555
+ invoke(
556
+ share,
557
+ "share_files",
558
+ args: {
559
+ "files" => files,
560
+ "text" => text,
561
+ "title" => title,
562
+ "subject" => subject,
563
+ "preview_thumbnail" => preview_thumbnail,
564
+ "share_position_origin" => share_position_origin,
565
+ "download_fallback_enabled" => download_fallback_enabled,
566
+ "mail_to_fallback_enabled" => mail_to_fallback_enabled,
567
+ "excluded_cupertino_activities" => excluded_cupertino_activities
568
+ },
569
+ timeout: timeout,
570
+ on_result: on_result
571
+ )
301
572
  end
302
573
 
303
574
  def handle_invoke_method_result(payload)
304
575
  call_id = payload["call_id"].to_s
305
576
  waiter = @invoke_waiters_mutex.synchronize { @invoke_waiters[call_id] }
306
- return false unless waiter
577
+ if waiter
578
+ waiter << payload
579
+ return true
580
+ end
307
581
 
308
- waiter << payload
582
+ callback = @invoke_waiters_mutex.synchronize { @invoke_callbacks.delete(call_id) }
583
+ return false unless callback
584
+
585
+ callback.call(payload["result"], payload["error"])
309
586
  true
587
+ rescue StandardError => e
588
+ Kernel.warn("invoke callback error: #{e.class}: #{e.message}")
589
+ false
310
590
  end
311
591
 
312
592
  def pop_dialog
@@ -333,9 +613,16 @@ module Ruflet
333
613
 
334
614
  control = resolve_control(control_or_id)
335
615
  return self unless control
616
+ wire_id = control.wire_id
617
+ if wire_id.nil?
618
+ # Events can race with navigation/disposal; never emit patch_control with nil id.
619
+ refresh_control_indexes!
620
+ wire_id = control.wire_id
621
+ end
622
+ return self if wire_id.nil?
336
623
 
337
624
  patch = normalize_props(props)
338
- if BUTTON_TEXT_TYPES.include?(control.type) && patch.key?("text")
625
+ if text_maps_to_content?(control, patch)
339
626
  patch["content"] = patch.delete("text")
340
627
  end
341
628
 
@@ -351,7 +638,7 @@ module Ruflet
351
638
  patch_ops = patch.map { |k, v| [0, 0, k, serialize_patch_value(v)] }
352
639
 
353
640
  send_message(Protocol::ACTIONS[:patch_control], {
354
- "id" => control.wire_id,
641
+ "id" => wire_id,
355
642
  "patch" => [[0], *patch_ops]
356
643
  })
357
644
 
@@ -400,8 +687,7 @@ module Ruflet
400
687
  prop_name = method_name.delete_suffix("=")
401
688
 
402
689
  if method_name.end_with?("=")
403
- if DEPRECATED_PAGE_WIDGET_METHODS.include?(prop_name.to_sym)
404
- Kernel.warn("[DEPRECATION] `page.#{prop_name}(...)` is no longer supported.")
690
+ if widget_helper_method?(prop_name)
405
691
  raise NoMethodError, "Use `#{prop_name}(...)` as a free widget helper, then attach with `page.add(...)`."
406
692
  end
407
693
  assign_split_prop(prop_name, normalize_value(prop_name, args.first))
@@ -414,8 +700,7 @@ module Ruflet
414
700
  return instance_variable_get("@#{method_name}") if DIALOG_PROP_KEYS.include?(method_name)
415
701
  end
416
702
 
417
- if DEPRECATED_PAGE_WIDGET_METHODS.include?(name.to_sym)
418
- Kernel.warn("[DEPRECATION] `page.#{name}(...)` is no longer supported.")
703
+ if widget_helper_method?(name)
419
704
  raise NoMethodError, "Use `#{name}(...)` as a free widget helper, then attach with `page.add(...)`."
420
705
  end
421
706
 
@@ -425,8 +710,8 @@ module Ruflet
425
710
  def respond_to_missing?(name, include_private = false)
426
711
  method_name = name.to_s
427
712
  prop_name = method_name.delete_suffix("=")
428
- DEPRECATED_PAGE_WIDGET_METHODS.include?(name.to_sym) ||
429
- DEPRECATED_PAGE_WIDGET_METHODS.include?(prop_name.to_sym) ||
713
+ widget_helper_method?(name) ||
714
+ widget_helper_method?(prop_name) ||
430
715
  method_name.end_with?("=") ||
431
716
  @page_props.key?(method_name) ||
432
717
  @view_props.key?(method_name) ||
@@ -437,15 +722,21 @@ module Ruflet
437
722
  private
438
723
 
439
724
  def invoke_and_wait(control_or_id, method_name, args: nil, timeout: 10)
440
- control = resolve_control(control_or_id)
441
- return nil unless control
725
+ control_id =
726
+ if page_control_target?(control_or_id)
727
+ 1
728
+ else
729
+ control = resolve_control(control_or_id)
730
+ return nil unless control
731
+ control.wire_id
732
+ end
442
733
 
443
734
  call_id = "call_#{Ruflet::Control.generate_id}"
444
735
  waiter = Queue.new
445
736
  @invoke_waiters_mutex.synchronize { @invoke_waiters[call_id] = waiter }
446
737
 
447
738
  send_message(Protocol::ACTIONS[:invoke_control_method], {
448
- "control_id" => control.wire_id,
739
+ "control_id" => control_id,
449
740
  "call_id" => call_id,
450
741
  "name" => method_name.to_s,
451
742
  "args" => args,
@@ -463,6 +754,14 @@ module Ruflet
463
754
 
464
755
  def build_widget(type, **props, &block) = WidgetBuilder.new.control(type, **props, &block)
465
756
 
757
+ def widget_helper_method?(name)
758
+ WIDGET_HELPER_METHODS.include?(name.to_s)
759
+ end
760
+
761
+ def text_maps_to_content?(control, patch)
762
+ patch.key?("text") && control.type.end_with?("button")
763
+ end
764
+
466
765
  def split_props(props)
467
766
  props.each do |k, v|
468
767
  assign_split_prop(k, v)
@@ -709,7 +1008,15 @@ module Ruflet
709
1008
  end
710
1009
 
711
1010
  def build_page_patch_ops
712
- @page_props.map { |k, v| [0, 0, k, serialize_patch_value(v)] }
1011
+ @page_props.filter_map do |k, v|
1012
+ # Keep internal containers stable after initial mount.
1013
+ # Re-sending them as full objects can replace Control instances with
1014
+ # same IDs and detach service invoke listeners on the Flutter side.
1015
+ next nil if k == "_overlay" && @overlay_container.wire_id
1016
+ next nil if k == "_dialogs" && @dialogs_container.wire_id
1017
+
1018
+ [0, 0, k, serialize_patch_value(v)]
1019
+ end
713
1020
  end
714
1021
 
715
1022
  def resolve_icon_codepoint(value)
@@ -720,22 +1027,196 @@ module Ruflet
720
1027
  codepoint
721
1028
  end
722
1029
 
723
- def ensure_url_launcher_service
724
- launcher = services.find { |service| service.is_a?(Control) && service.type == "url_launcher" }
725
- return launcher if launcher
726
-
727
- launcher = build_widget(:url_launcher)
728
- add_service(launcher)
729
- launcher
730
- end
731
-
732
1030
  def ensure_clipboard_service
733
1031
  clipboard = services.find { |service| service.is_a?(Control) && service.type == "clipboard" }
734
- return clipboard if clipboard
1032
+ return [clipboard, false] if clipboard
735
1033
 
736
1034
  clipboard = build_widget(:clipboard)
737
1035
  add_service(clipboard)
738
- clipboard
1036
+ [clipboard, true]
1037
+ end
1038
+
1039
+ def invoke_clipboard_method(method_name, args: nil, timeout:, on_result:)
1040
+ clipboard, created = ensure_clipboard_service
1041
+ send_view_patch if created
1042
+ sleep(0.05) if created
1043
+ invoke(
1044
+ clipboard,
1045
+ method_name,
1046
+ args: args,
1047
+ timeout: timeout,
1048
+ on_result: lambda { |result, error|
1049
+ message = error.to_s
1050
+ if message.include?("inexistent control")
1051
+ remove_service(clipboard)
1052
+ fresh_clipboard, = ensure_clipboard_service
1053
+ sleep(0.08)
1054
+ invoke(
1055
+ fresh_clipboard,
1056
+ method_name,
1057
+ args: args,
1058
+ timeout: timeout,
1059
+ on_result: on_result
1060
+ )
1061
+ else
1062
+ on_result&.call(result, error)
1063
+ end
1064
+ }
1065
+ )
1066
+ rescue StandardError => e
1067
+ on_result&.call(nil, e.message)
1068
+ end
1069
+
1070
+ def ensure_url_launcher_service
1071
+ url_launcher = services.find { |service| service.is_a?(Control) && %w[urllauncher url_launcher].include?(service.type) }
1072
+ return url_launcher if url_launcher
1073
+
1074
+ url_launcher = build_widget(:url_launcher)
1075
+ add_service(url_launcher)
1076
+ url_launcher
1077
+ end
1078
+
1079
+ def ensure_connectivity_service
1080
+ connectivity = services.find { |service| service.is_a?(Control) && service.type == "connectivity" }
1081
+ return [connectivity, false] if connectivity
1082
+
1083
+ connectivity = build_widget(:connectivity)
1084
+ add_service(connectivity)
1085
+ [connectivity, true]
1086
+ end
1087
+
1088
+ def invoke_connectivity_method(method_name, timeout:, on_result:)
1089
+ connectivity, created = ensure_connectivity_service
1090
+ send_view_patch if created
1091
+ sleep(0.05) if created
1092
+ invoke(
1093
+ connectivity,
1094
+ method_name,
1095
+ timeout: timeout,
1096
+ on_result: lambda { |result, error|
1097
+ message = error.to_s
1098
+ if message.include?("inexistent control")
1099
+ remove_service(connectivity)
1100
+ fresh_connectivity, = ensure_connectivity_service
1101
+ sleep(0.08)
1102
+ invoke(
1103
+ fresh_connectivity,
1104
+ method_name,
1105
+ timeout: timeout,
1106
+ on_result: on_result
1107
+ )
1108
+ else
1109
+ on_result&.call(result, error)
1110
+ end
1111
+ }
1112
+ )
1113
+ rescue StandardError => e
1114
+ on_result&.call(nil, e.message)
1115
+ end
1116
+
1117
+ def ensure_battery_service
1118
+ battery = services.find { |service| service.is_a?(Control) && service.type == "battery" }
1119
+ return [battery, false] if battery
1120
+
1121
+ battery = build_widget(:battery)
1122
+ add_service(battery)
1123
+ [battery, true]
1124
+ end
1125
+
1126
+ def ensure_share_service
1127
+ share = services.find { |service| service.is_a?(Control) && service.type == "share" }
1128
+ return share if share
1129
+
1130
+ share = build_widget(:share)
1131
+ add_service(share)
1132
+ share
1133
+ end
1134
+
1135
+ def invoke_battery_method(method_name, timeout:, on_result:)
1136
+ battery, created = ensure_battery_service
1137
+ send_view_patch if created
1138
+ sleep(0.05) if created
1139
+ invoke(
1140
+ battery,
1141
+ method_name,
1142
+ timeout: timeout,
1143
+ on_result: lambda { |result, error|
1144
+ message = error.to_s
1145
+ if message.include?("inexistent control")
1146
+ remove_service(battery)
1147
+ fresh_battery, = ensure_battery_service
1148
+ sleep(0.08)
1149
+ invoke(
1150
+ fresh_battery,
1151
+ method_name,
1152
+ timeout: timeout,
1153
+ on_result: on_result
1154
+ )
1155
+ else
1156
+ on_result&.call(result, error)
1157
+ end
1158
+ }
1159
+ )
1160
+ rescue StandardError => e
1161
+ on_result&.call(nil, e.message)
1162
+ end
1163
+
1164
+ def ensure_storage_paths_service
1165
+ storage_paths = services.find do |service|
1166
+ service.is_a?(Control) && %w[storage_paths storagepaths].include?(service.type.to_s)
1167
+ end
1168
+ return [storage_paths, false] if storage_paths
1169
+
1170
+ storage_paths = build_widget(:storage_paths)
1171
+ add_service(storage_paths)
1172
+ [storage_paths, true]
1173
+ end
1174
+
1175
+ def invoke_storage_paths(method_name, timeout:, on_result:)
1176
+ storage_paths, created = ensure_storage_paths_service
1177
+ send_view_patch if created
1178
+ sleep(0.05) if created
1179
+ invoke(
1180
+ storage_paths,
1181
+ method_name,
1182
+ timeout: timeout,
1183
+ on_result: lambda { |result, error|
1184
+ message = error.to_s
1185
+ if message.include?("inexistent control")
1186
+ remove_service(storage_paths)
1187
+ fresh_storage_paths, = ensure_storage_paths_service
1188
+ sleep(0.08)
1189
+ invoke(
1190
+ fresh_storage_paths,
1191
+ method_name,
1192
+ timeout: timeout,
1193
+ on_result: on_result
1194
+ )
1195
+ else
1196
+ on_result&.call(result, error)
1197
+ end
1198
+ }
1199
+ )
1200
+ rescue StandardError => e
1201
+ on_result&.call(nil, e.message)
1202
+ end
1203
+
1204
+ def invoke_file_picker(method_name, args, timeout:, on_result:)
1205
+ picker = build_widget(:file_picker)
1206
+ add_service(picker)
1207
+ invoke(
1208
+ picker,
1209
+ method_name,
1210
+ args: args,
1211
+ timeout: timeout,
1212
+ on_result: lambda { |result, error|
1213
+ remove_service(picker)
1214
+ on_result&.call(result, error)
1215
+ }
1216
+ )
1217
+ rescue StandardError => e
1218
+ remove_service(picker) if picker
1219
+ on_result&.call(nil, e.message)
739
1220
  end
740
1221
  end
741
1222
  end