ruflet_core 0.0.16 → 0.0.18
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.
- checksums.yaml +4 -4
- data/README.md +2 -18
- data/lib/ruflet/version.rb +1 -1
- data/lib/ruflet_core.rb +9 -7
- data/lib/ruflet_protocol/ruflet/protocol.rb +28 -54
- data/lib/ruflet_ui/ruflet/app.rb +1 -1
- data/lib/ruflet_ui/ruflet/colors.rb +4 -18
- data/lib/ruflet_ui/ruflet/control.rb +65 -63
- data/lib/ruflet_ui/ruflet/dsl.rb +10 -3
- data/lib/ruflet_ui/ruflet/icons/cupertino/cupertino_icons.rb +2 -2
- data/lib/ruflet_ui/ruflet/icons/cupertino_icon_lookup.rb +1 -0
- data/lib/ruflet_ui/ruflet/icons/icon_constant_names.rb +29 -0
- data/lib/ruflet_ui/ruflet/icons/material/material_icons.rb +2 -2
- data/lib/ruflet_ui/ruflet/icons/material_icon_lookup.rb +1 -0
- data/lib/ruflet_ui/ruflet/page.rb +303 -460
- data/lib/ruflet_ui/ruflet/ui/control_factory.rb +68 -12
- data/lib/ruflet_ui/ruflet/ui/control_registry.rb +13 -6
- data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinoactionsheet_control.rb +8 -45
- data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinoactionsheetaction_control.rb +45 -2
- data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinoactivityindicator_control.rb +43 -2
- data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinoalertdialog_control.rb +0 -5
- data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinobutton_control.rb +59 -4
- data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinocheckbox_control.rb +56 -1
- data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinocontextmenu_control.rb +1 -3
- data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinocontextmenuaction_control.rb +1 -1
- data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinodatepicker_control.rb +53 -8
- data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinodialogaction_control.rb +1 -1
- data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinofilledbutton_control.rb +59 -4
- data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinolisttile_control.rb +54 -9
- data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinonavigationbar_control.rb +47 -9
- data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinopicker_control.rb +52 -10
- data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinoradio_control.rb +52 -1
- data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinosegmentedbutton_control.rb +49 -1
- data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinoslider_control.rb +50 -5
- data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinoslidingsegmentedbutton_control.rb +46 -1
- data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinoswitch_control.rb +61 -1
- data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinotextfield_control.rb +145 -1
- data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinotimerpicker_control.rb +47 -12
- data/lib/ruflet_ui/ruflet/ui/controls/cupertinos/cupertinotintedbutton_control.rb +59 -4
- data/lib/ruflet_ui/ruflet/ui/controls/materials/alertdialog_control.rb +8 -45
- data/lib/ruflet_ui/ruflet/ui/controls/materials/appbar_control.rb +8 -40
- data/lib/ruflet_ui/ruflet/ui/controls/materials/audio_control.rb +16 -5
- data/lib/ruflet_ui/ruflet/ui/controls/materials/autocomplete_control.rb +44 -1
- data/lib/ruflet_ui/ruflet/ui/controls/materials/badge_control.rb +0 -7
- data/lib/ruflet_ui/ruflet/ui/controls/materials/banner_control.rb +0 -7
- data/lib/ruflet_ui/ruflet/ui/controls/materials/bottomappbar_control.rb +8 -47
- data/lib/ruflet_ui/ruflet/ui/controls/materials/bottomsheet_control.rb +0 -3
- data/lib/ruflet_ui/ruflet/ui/controls/materials/button_control.rb +8 -54
- data/lib/ruflet_ui/ruflet/ui/controls/materials/card_control.rb +8 -50
- data/lib/ruflet_ui/ruflet/ui/controls/materials/chart_controls.rb +19 -0
- data/lib/ruflet_ui/ruflet/ui/controls/materials/checkbox_control.rb +9 -60
- data/lib/ruflet_ui/ruflet/ui/controls/materials/chip_control.rb +7 -81
- data/lib/ruflet_ui/ruflet/ui/controls/materials/circleavatar_control.rb +7 -56
- data/lib/ruflet_ui/ruflet/ui/controls/materials/container_control.rb +9 -65
- data/lib/ruflet_ui/ruflet/ui/controls/materials/contextmenu_control.rb +50 -4
- data/lib/ruflet_ui/ruflet/ui/controls/materials/datacell_control.rb +1 -3
- data/lib/ruflet_ui/ruflet/ui/controls/materials/datacolumn_control.rb +1 -3
- data/lib/ruflet_ui/ruflet/ui/controls/materials/datatable_control.rb +7 -88
- data/lib/ruflet_ui/ruflet/ui/controls/materials/datepicker_control.rb +9 -55
- data/lib/ruflet_ui/ruflet/ui/controls/materials/daterangepicker_control.rb +8 -58
- data/lib/ruflet_ui/ruflet/ui/controls/materials/divider_control.rb +0 -9
- data/lib/ruflet_ui/ruflet/ui/controls/materials/dropdown_control.rb +8 -93
- data/lib/ruflet_ui/ruflet/ui/controls/materials/dropdownm2_control.rb +9 -103
- data/lib/ruflet_ui/ruflet/ui/controls/materials/dropdownoption_control.rb +0 -2
- data/lib/ruflet_ui/ruflet/ui/controls/materials/expansionpanel_control.rb +7 -51
- data/lib/ruflet_ui/ruflet/ui/controls/materials/expansionpanellist_control.rb +46 -5
- data/lib/ruflet_ui/ruflet/ui/controls/materials/expansiontile_control.rb +69 -3
- data/lib/ruflet_ui/ruflet/ui/controls/materials/filledbutton_control.rb +54 -2
- data/lib/ruflet_ui/ruflet/ui/controls/materials/fillediconbutton_control.rb +67 -5
- data/lib/ruflet_ui/ruflet/ui/controls/materials/filledtonalbutton_control.rb +54 -2
- data/lib/ruflet_ui/ruflet/ui/controls/materials/filledtonaliconbutton_control.rb +67 -5
- data/lib/ruflet_ui/ruflet/ui/controls/materials/floatingactionbutton_control.rb +7 -69
- data/lib/ruflet_ui/ruflet/ui/controls/materials/iconbutton_control.rb +8 -68
- data/lib/ruflet_ui/ruflet/ui/controls/materials/listtile_control.rb +8 -74
- data/lib/ruflet_ui/ruflet/ui/controls/materials/map_control.rb +99 -0
- data/lib/ruflet_ui/ruflet/ui/controls/materials/menuitembutton_control.rb +8 -56
- data/lib/ruflet_ui/ruflet/ui/controls/materials/navigationbar_control.rb +7 -59
- data/lib/ruflet_ui/ruflet/ui/controls/materials/navigationdrawer_control.rb +0 -7
- data/lib/ruflet_ui/ruflet/ui/controls/materials/navigationrail_control.rb +7 -73
- data/lib/ruflet_ui/ruflet/ui/controls/materials/outlinedbutton_control.rb +51 -2
- data/lib/ruflet_ui/ruflet/ui/controls/materials/outlinediconbutton_control.rb +67 -5
- data/lib/ruflet_ui/ruflet/ui/controls/materials/popupmenubutton_control.rb +8 -61
- data/lib/ruflet_ui/ruflet/ui/controls/materials/popupmenuitem_control.rb +0 -6
- data/lib/ruflet_ui/ruflet/ui/controls/materials/progressbar_control.rb +8 -56
- data/lib/ruflet_ui/ruflet/ui/controls/materials/progressring_control.rb +8 -56
- data/lib/ruflet_ui/ruflet/ui/controls/materials/radio_control.rb +9 -54
- data/lib/ruflet_ui/ruflet/ui/controls/materials/rangeslider_control.rb +7 -62
- data/lib/ruflet_ui/ruflet/ui/controls/materials/reorderablelistview_control.rb +9 -64
- data/lib/ruflet_ui/ruflet/ui/controls/materials/ruflet_controls.rb +5 -26
- data/lib/ruflet_ui/ruflet/ui/controls/materials/searchbar_control.rb +80 -11
- data/lib/ruflet_ui/ruflet/ui/controls/materials/segment_control.rb +0 -3
- data/lib/ruflet_ui/ruflet/ui/controls/materials/segmentedbutton_control.rb +7 -64
- data/lib/ruflet_ui/ruflet/ui/controls/materials/selectionarea_control.rb +1 -3
- data/lib/ruflet_ui/ruflet/ui/controls/materials/slider_control.rb +9 -60
- data/lib/ruflet_ui/ruflet/ui/controls/materials/snackbar_control.rb +37 -4
- data/lib/ruflet_ui/ruflet/ui/controls/materials/snackbaraction_control.rb +38 -0
- data/lib/ruflet_ui/ruflet/ui/controls/materials/spinkit_control.rb +25 -0
- data/lib/ruflet_ui/ruflet/ui/controls/materials/submenubutton_control.rb +52 -7
- data/lib/ruflet_ui/ruflet/ui/controls/materials/switch_control.rb +9 -61
- data/lib/ruflet_ui/ruflet/ui/controls/materials/tab_control.rb +0 -7
- data/lib/ruflet_ui/ruflet/ui/controls/materials/tabbar_control.rb +8 -64
- data/lib/ruflet_ui/ruflet/ui/controls/materials/tabbarview_control.rb +43 -5
- data/lib/ruflet_ui/ruflet/ui/controls/materials/tabs_control.rb +7 -47
- data/lib/ruflet_ui/ruflet/ui/controls/materials/textbutton_control.rb +51 -2
- data/lib/ruflet_ui/ruflet/ui/controls/materials/textfield_control.rb +9 -132
- data/lib/ruflet_ui/ruflet/ui/controls/materials/timepicker_control.rb +10 -34
- data/lib/ruflet_ui/ruflet/ui/controls/materials/verticaldivider_control.rb +0 -9
- data/lib/ruflet_ui/ruflet/ui/controls/materials/video_control.rb +9 -42
- data/lib/ruflet_ui/ruflet/ui/controls/materials/webview_control.rb +1 -114
- data/lib/ruflet_ui/ruflet/ui/controls/ruflet_controls.rb +13 -28
- data/lib/ruflet_ui/ruflet/ui/controls/shared/animatedswitcher_control.rb +46 -4
- data/lib/ruflet_ui/ruflet/ui/controls/shared/arc_control.rb +1 -0
- data/lib/ruflet_ui/ruflet/ui/controls/shared/browsercontextmenu_control.rb +1 -12
- data/lib/ruflet_ui/ruflet/ui/controls/shared/canvas_control.rb +8 -54
- data/lib/ruflet_ui/ruflet/ui/controls/shared/circle_control.rb +1 -0
- data/lib/ruflet_ui/ruflet/ui/controls/shared/column_control.rb +9 -51
- data/lib/ruflet_ui/ruflet/ui/controls/shared/dismissible_control.rb +52 -12
- data/lib/ruflet_ui/ruflet/ui/controls/shared/draggable_control.rb +1 -7
- data/lib/ruflet_ui/ruflet/ui/controls/shared/dragtarget_control.rb +1 -3
- data/lib/ruflet_ui/ruflet/ui/controls/shared/fletapp_control.rb +9 -46
- data/lib/ruflet_ui/ruflet/ui/controls/shared/gesturedetector_control.rb +9 -113
- data/lib/ruflet_ui/ruflet/ui/controls/shared/gridview_control.rb +57 -13
- data/lib/ruflet_ui/ruflet/ui/controls/shared/hero_control.rb +9 -40
- data/lib/ruflet_ui/ruflet/ui/controls/shared/icon_control.rb +9 -48
- data/lib/ruflet_ui/ruflet/ui/controls/shared/interactiveviewer_control.rb +9 -53
- data/lib/ruflet_ui/ruflet/ui/controls/shared/line_control.rb +1 -0
- data/lib/ruflet_ui/ruflet/ui/controls/shared/listview_control.rb +57 -11
- data/lib/ruflet_ui/ruflet/ui/controls/shared/markdown_control.rb +9 -54
- data/lib/ruflet_ui/ruflet/ui/controls/shared/oval_control.rb +1 -0
- data/lib/ruflet_ui/ruflet/ui/controls/shared/page_control.rb +9 -58
- data/lib/ruflet_ui/ruflet/ui/controls/shared/pagelet_control.rb +9 -48
- data/lib/ruflet_ui/ruflet/ui/controls/shared/pageview_control.rb +50 -4
- data/lib/ruflet_ui/ruflet/ui/controls/shared/path_control.rb +1 -0
- data/lib/ruflet_ui/ruflet/ui/controls/shared/placeholder_control.rb +44 -9
- data/lib/ruflet_ui/ruflet/ui/controls/shared/points_control.rb +1 -0
- data/lib/ruflet_ui/ruflet/ui/controls/shared/rect_control.rb +1 -0
- data/lib/ruflet_ui/ruflet/ui/controls/shared/reorderabledraghandle_control.rb +43 -4
- data/lib/ruflet_ui/ruflet/ui/controls/shared/responsiverow_control.rb +47 -15
- data/lib/ruflet_ui/ruflet/ui/controls/shared/rotatedbox_control.rb +97 -0
- data/lib/ruflet_ui/ruflet/ui/controls/shared/row_control.rb +9 -51
- data/lib/ruflet_ui/ruflet/ui/controls/shared/ruflet_controls.rb +5 -0
- data/lib/ruflet_ui/ruflet/ui/controls/shared/safearea_control.rb +48 -4
- data/lib/ruflet_ui/ruflet/ui/{services/ruflet → controls/shared}/screenshot_control.rb +18 -2
- data/lib/ruflet_ui/ruflet/ui/controls/shared/semantics_control.rb +65 -1
- data/lib/ruflet_ui/ruflet/ui/controls/shared/shadermask_control.rb +9 -41
- data/lib/ruflet_ui/ruflet/ui/controls/shared/shimmer_control.rb +9 -44
- data/lib/ruflet_ui/ruflet/ui/controls/shared/stack_control.rb +44 -1
- data/lib/ruflet_ui/ruflet/ui/controls/shared/text_control.rb +9 -35
- data/lib/ruflet_ui/ruflet/ui/controls/shared/transparentpointer_control.rb +9 -38
- data/lib/ruflet_ui/ruflet/ui/controls/shared/view_control.rb +63 -1
- data/lib/ruflet_ui/ruflet/ui/controls/shared/window_control.rb +40 -66
- data/lib/ruflet_ui/ruflet/ui/controls/shared/windowdragarea_control.rb +45 -4
- data/lib/ruflet_ui/ruflet/ui/material_control_methods.rb +61 -17
- data/lib/ruflet_ui/ruflet/ui/material_control_registry.rb +22 -0
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/accelerometer_control.rb +1 -0
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/audio_recorder_control.rb +14 -36
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/barometer_control.rb +1 -0
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/battery_control.rb +1 -0
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/camera_control.rb +9 -42
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/clipboard_control.rb +31 -0
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/connectivity_control.rb +1 -0
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/filepicker_control.rb +1 -0
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/flashlight_control.rb +1 -0
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/geolocator_control.rb +14 -30
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/gyroscope_control.rb +1 -0
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/hapticfeedback_control.rb +1 -0
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/magnetometer_control.rb +1 -0
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/permissionhandler_control.rb +5 -20
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/screenbrightness_control.rb +1 -0
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/securestorage_control.rb +22 -49
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/semanticsservice_control.rb +20 -15
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/shakedetector_control.rb +1 -0
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/share_control.rb +1 -0
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/sharedpreferences_control.rb +1 -0
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/storagepaths_control.rb +1 -0
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/urllauncher_control.rb +1 -0
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/useraccelerometer_control.rb +1 -0
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/wakelock_control.rb +1 -0
- data/lib/ruflet_ui/ruflet/ui/services/ruflet_services.rb +0 -7
- data/lib/ruflet_ui/ruflet/ui/shared_control_forwarders.rb +20 -1
- data/lib/ruflet_ui.rb +12 -10
- metadata +8 -9
- data/lib/ruflet_ui/ruflet/types/animation.rb +0 -110
- data/lib/ruflet_ui/ruflet/ui/controls/materials/codeeditor_control.rb +0 -93
- data/lib/ruflet_ui/ruflet/ui/controls/materials/map_controls.rb +0 -311
- data/lib/ruflet_ui/ruflet/ui/controls/materials/rive_control.rb +0 -87
- data/lib/ruflet_ui/ruflet/ui/controls/materials/spinkit_controls.rb +0 -109
- data/lib/ruflet_ui/ruflet/ui/services/ruflet/tester_control.rb +0 -126
|
@@ -9,11 +9,21 @@ require_relative "icons/material_icon_lookup"
|
|
|
9
9
|
require_relative "icons/cupertino_icon_lookup"
|
|
10
10
|
require "set"
|
|
11
11
|
require "cgi"
|
|
12
|
+
require "uri"
|
|
12
13
|
require "thread"
|
|
13
14
|
require "timeout"
|
|
14
15
|
|
|
15
16
|
module Ruflet
|
|
16
17
|
class Page
|
|
18
|
+
# Older and embedded clients may omit this capability from registration.
|
|
19
|
+
# Ruflet applications should still be able to branch on `page.web` just as
|
|
20
|
+
# they do with a full desktop client.
|
|
21
|
+
def web
|
|
22
|
+
!!(@client_details && @client_details["web"])
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
alias web? web
|
|
26
|
+
|
|
17
27
|
class SharedPreferencesService
|
|
18
28
|
def initialize(page)
|
|
19
29
|
@page = page
|
|
@@ -139,15 +149,16 @@ module Ruflet
|
|
|
139
149
|
end
|
|
140
150
|
end
|
|
141
151
|
|
|
142
|
-
PAGE_PROP_KEYS = %w[dark_theme fonts route rtl show_semantics_debugger theme theme_mode title vertical_alignment horizontal_alignment scroll].freeze
|
|
152
|
+
PAGE_PROP_KEYS = %w[dark_theme fonts route rtl show_semantics_debugger theme theme_mode theme_animation_style title vertical_alignment horizontal_alignment scroll].freeze
|
|
143
153
|
DIALOG_PROP_KEYS = %w[dialog snack_bar bottom_sheet].freeze
|
|
154
|
+
PAGE_ADD_RESERVED_KEYS = %i[appbar bottom_appbar floating_action_button navigation_bar dialog snack_bar bottom_sheet].freeze
|
|
144
155
|
WIDGET_HELPER_METHODS = (
|
|
145
156
|
Ruflet::UI::MaterialControlMethods.instance_methods(false) +
|
|
146
157
|
Ruflet::UI::CupertinoControlMethods.instance_methods(false) +
|
|
147
158
|
%i[control widget]
|
|
148
|
-
).map(&:to_s).
|
|
159
|
+
).map(&:to_s).uniq.freeze
|
|
149
160
|
|
|
150
|
-
attr_reader :session_id, :client_details, :views
|
|
161
|
+
attr_reader :session_id, :client_details, :views, :window
|
|
151
162
|
|
|
152
163
|
def initialize(session_id:, client_details:, sender:)
|
|
153
164
|
@session_id = session_id
|
|
@@ -167,6 +178,8 @@ module Ruflet
|
|
|
167
178
|
@page_event_handlers = {}
|
|
168
179
|
@view_props = {}
|
|
169
180
|
@page_props = { "route" => (client_details["route"] || "/") }
|
|
181
|
+
@window = build_client_window(client_details["window"])
|
|
182
|
+
@page_props["window"] = @window
|
|
170
183
|
@overlay_container = Ruflet::Control.new(
|
|
171
184
|
type: "overlay",
|
|
172
185
|
id: "_overlay",
|
|
@@ -244,64 +257,64 @@ module Ruflet
|
|
|
244
257
|
@view_props["bgcolor"] = normalize_value("bgcolor", value)
|
|
245
258
|
end
|
|
246
259
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
def width
|
|
252
|
-
client_reported_prop("width")
|
|
253
|
-
end
|
|
260
|
+
def add(control)
|
|
261
|
+
if control.is_a?(Hash) && (control.keys.map(&:to_sym) & PAGE_ADD_RESERVED_KEYS).any?
|
|
262
|
+
raise ArgumentError, "Page#add accepts only controls; assign page.appbar, page.floating_action_button, dialogs, or other page properties before calling add"
|
|
263
|
+
end
|
|
254
264
|
|
|
255
|
-
|
|
256
|
-
client_reported_prop("height")
|
|
257
|
-
end
|
|
265
|
+
raise ArgumentError, "Page#add accepts exactly one control" unless control.is_a?(Control)
|
|
258
266
|
|
|
259
|
-
|
|
260
|
-
|
|
267
|
+
replace_root_controls(@root_controls + [control])
|
|
268
|
+
self
|
|
261
269
|
end
|
|
262
270
|
|
|
263
|
-
def
|
|
264
|
-
|
|
271
|
+
def controls
|
|
272
|
+
@root_controls
|
|
265
273
|
end
|
|
266
274
|
|
|
267
|
-
def
|
|
268
|
-
|
|
275
|
+
def controls=(value)
|
|
276
|
+
replace_root_controls(Array(value).flatten.compact)
|
|
277
|
+
self
|
|
269
278
|
end
|
|
270
279
|
|
|
271
|
-
def
|
|
272
|
-
|
|
280
|
+
def insert(at, *controls)
|
|
281
|
+
@root_controls.insert(at.to_i, *controls.flatten.compact)
|
|
282
|
+
send_view_patch
|
|
283
|
+
self
|
|
273
284
|
end
|
|
274
285
|
|
|
275
|
-
def
|
|
276
|
-
|
|
286
|
+
def remove(*controls)
|
|
287
|
+
controls.flatten.each { |control| @root_controls.delete(control) }
|
|
288
|
+
send_view_patch
|
|
289
|
+
self
|
|
277
290
|
end
|
|
278
291
|
|
|
279
|
-
def
|
|
280
|
-
|
|
292
|
+
def remove_at(index)
|
|
293
|
+
@root_controls.delete_at(index.to_i)
|
|
294
|
+
send_view_patch
|
|
295
|
+
self
|
|
281
296
|
end
|
|
282
297
|
|
|
283
|
-
def
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
@root_controls = controls
|
|
288
|
-
|
|
289
|
-
update_view_slot("appbar", appbar)
|
|
290
|
-
update_view_slot("bottom_appbar", bottom_appbar)
|
|
291
|
-
update_view_slot("floating_action_button", floating_action_button)
|
|
292
|
-
update_view_slot("navigation_bar", navigation_bar)
|
|
293
|
-
@dialog = dialog if dialog
|
|
294
|
-
@snack_bar = snack_bar if snack_bar
|
|
295
|
-
@bottom_sheet = bottom_sheet if bottom_sheet
|
|
296
|
-
|
|
297
|
-
refresh_dialogs_container!
|
|
298
|
-
@view_props.each_value { |value| register_embedded_value(value, visited) }
|
|
298
|
+
def clean
|
|
299
|
+
replace_root_controls([])
|
|
300
|
+
self
|
|
301
|
+
end
|
|
299
302
|
|
|
300
|
-
|
|
303
|
+
def overlay
|
|
304
|
+
@overlay_container.children
|
|
305
|
+
end
|
|
301
306
|
|
|
307
|
+
def overlay=(value)
|
|
308
|
+
@overlay_container.children.replace(Array(value).flatten.compact)
|
|
309
|
+
push_overlay_update!
|
|
302
310
|
self
|
|
303
311
|
end
|
|
304
312
|
|
|
313
|
+
def get_control(id)
|
|
314
|
+
refresh_control_indexes!
|
|
315
|
+
resolve_control(id)
|
|
316
|
+
end
|
|
317
|
+
|
|
305
318
|
def views=(value)
|
|
306
319
|
@views = Array(value).compact
|
|
307
320
|
self
|
|
@@ -350,18 +363,6 @@ module Ruflet
|
|
|
350
363
|
service(:audio_recorder, **props)
|
|
351
364
|
end
|
|
352
365
|
|
|
353
|
-
def browser_context_menu(**props)
|
|
354
|
-
service(:browser_context_menu, **props)
|
|
355
|
-
end
|
|
356
|
-
|
|
357
|
-
def window(**props)
|
|
358
|
-
service(:window, **props)
|
|
359
|
-
end
|
|
360
|
-
|
|
361
|
-
def tester(**props)
|
|
362
|
-
service(:tester, **props)
|
|
363
|
-
end
|
|
364
|
-
|
|
365
366
|
def add_service(*value)
|
|
366
367
|
@services_container.props["_services"] = services + value.flatten.compact
|
|
367
368
|
refresh_services_container!
|
|
@@ -406,6 +407,10 @@ module Ruflet
|
|
|
406
407
|
return svc
|
|
407
408
|
end
|
|
408
409
|
|
|
410
|
+
unless service_control_type?(normalized_type)
|
|
411
|
+
raise ArgumentError, "#{type} is a visual control, not a page service"
|
|
412
|
+
end
|
|
413
|
+
|
|
409
414
|
existing =
|
|
410
415
|
if id
|
|
411
416
|
services.find { |s| s.is_a?(Control) && s.id.to_s == id.to_s }
|
|
@@ -414,7 +419,14 @@ module Ruflet
|
|
|
414
419
|
s.is_a?(Control) && s.type.to_s.downcase.delete("_") == compact_type
|
|
415
420
|
end
|
|
416
421
|
end
|
|
417
|
-
|
|
422
|
+
if existing
|
|
423
|
+
unless mapped_props.empty?
|
|
424
|
+
existing.merge_props(mapped_props)
|
|
425
|
+
refresh_services_container!
|
|
426
|
+
push_services_update!
|
|
427
|
+
end
|
|
428
|
+
return existing
|
|
429
|
+
end
|
|
418
430
|
|
|
419
431
|
svc = Ruflet::UI::ControlFactory.build(type.to_s, id: id&.to_s, **mapped_props)
|
|
420
432
|
add_service(svc) unless services.include?(svc)
|
|
@@ -448,25 +460,35 @@ module Ruflet
|
|
|
448
460
|
@page_event_handlers["view_pop"] = handler
|
|
449
461
|
end
|
|
450
462
|
|
|
451
|
-
def
|
|
452
|
-
@page_event_handlers["
|
|
463
|
+
def on_views_pop_until=(handler)
|
|
464
|
+
@page_event_handlers["views_pop_until"] = handler
|
|
453
465
|
end
|
|
454
466
|
|
|
455
467
|
def on(event_name, &block)
|
|
456
|
-
|
|
468
|
+
name = event_name.to_s
|
|
469
|
+
name = name[3..-1] if name.start_with?("on_")
|
|
470
|
+
@page_event_handlers[name] = block
|
|
457
471
|
self
|
|
458
472
|
end
|
|
459
473
|
|
|
460
474
|
def mount(&block)
|
|
461
475
|
builder = WidgetBuilder.new
|
|
462
476
|
builder.instance_eval(&block)
|
|
463
|
-
|
|
477
|
+
builder.children.each { |control| add(control) }
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
def appbar
|
|
481
|
+
@view_props["appbar"]
|
|
464
482
|
end
|
|
465
483
|
|
|
466
484
|
def appbar=(value)
|
|
467
485
|
@view_props["appbar"] = value
|
|
468
486
|
end
|
|
469
487
|
|
|
488
|
+
def bottom_appbar
|
|
489
|
+
@view_props["bottom_appbar"]
|
|
490
|
+
end
|
|
491
|
+
|
|
470
492
|
def bottom_appbar=(value)
|
|
471
493
|
@view_props["bottom_appbar"] = value
|
|
472
494
|
end
|
|
@@ -475,48 +497,92 @@ module Ruflet
|
|
|
475
497
|
self.bottom_appbar = value
|
|
476
498
|
end
|
|
477
499
|
|
|
500
|
+
def floating_action_button
|
|
501
|
+
@view_props["floating_action_button"]
|
|
502
|
+
end
|
|
503
|
+
|
|
478
504
|
def floating_action_button=(value)
|
|
479
505
|
@view_props["floating_action_button"] = value
|
|
480
506
|
end
|
|
481
507
|
|
|
482
|
-
def
|
|
483
|
-
@view_props["
|
|
508
|
+
def navigation_bar
|
|
509
|
+
@view_props["navigation_bar"]
|
|
484
510
|
end
|
|
485
511
|
|
|
486
|
-
def
|
|
487
|
-
@view_props["
|
|
512
|
+
def navigation_bar=(value)
|
|
513
|
+
@view_props["navigation_bar"] = value
|
|
488
514
|
end
|
|
489
515
|
|
|
490
|
-
def
|
|
491
|
-
@view_props["
|
|
516
|
+
def auto_scroll
|
|
517
|
+
@view_props["auto_scroll"]
|
|
492
518
|
end
|
|
493
519
|
|
|
494
|
-
def
|
|
495
|
-
@view_props["
|
|
520
|
+
def auto_scroll=(value)
|
|
521
|
+
@view_props["auto_scroll"] = value
|
|
496
522
|
end
|
|
497
523
|
|
|
498
|
-
def
|
|
499
|
-
|
|
524
|
+
def browser_context_menu
|
|
525
|
+
@view_props["browser_context_menu"]
|
|
526
|
+
end
|
|
500
527
|
|
|
501
|
-
|
|
502
|
-
|
|
528
|
+
def browser_context_menu=(value)
|
|
529
|
+
@view_props["browser_context_menu"] = value
|
|
503
530
|
end
|
|
504
531
|
|
|
505
|
-
def
|
|
506
|
-
|
|
507
|
-
self
|
|
532
|
+
def decoration
|
|
533
|
+
@view_props["decoration"]
|
|
508
534
|
end
|
|
509
535
|
|
|
510
|
-
def
|
|
511
|
-
|
|
536
|
+
def decoration=(value)
|
|
537
|
+
@view_props["decoration"] = value
|
|
538
|
+
end
|
|
512
539
|
|
|
513
|
-
|
|
514
|
-
|
|
540
|
+
def floating_action_button_location
|
|
541
|
+
@view_props["floating_action_button_location"]
|
|
515
542
|
end
|
|
516
543
|
|
|
517
|
-
def
|
|
518
|
-
|
|
519
|
-
|
|
544
|
+
def floating_action_button_location=(value)
|
|
545
|
+
@view_props["floating_action_button_location"] = value
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
def foreground_decoration
|
|
549
|
+
@view_props["foreground_decoration"]
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
def foreground_decoration=(value)
|
|
553
|
+
@view_props["foreground_decoration"] = value
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
def padding
|
|
557
|
+
@view_props["padding"]
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
def padding=(value)
|
|
561
|
+
@view_props["padding"] = value
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
def spacing
|
|
565
|
+
@view_props["spacing"]
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
def spacing=(value)
|
|
569
|
+
@view_props["spacing"] = value
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
def drawer
|
|
573
|
+
@view_props["drawer"]
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
def drawer=(value)
|
|
577
|
+
@view_props["drawer"] = value
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
def end_drawer
|
|
581
|
+
@view_props["end_drawer"]
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
def end_drawer=(value)
|
|
585
|
+
@view_props["end_drawer"] = value
|
|
520
586
|
end
|
|
521
587
|
|
|
522
588
|
def dialog = @dialog
|
|
@@ -524,13 +590,15 @@ module Ruflet
|
|
|
524
590
|
def dialog=(value)
|
|
525
591
|
@dialog = value
|
|
526
592
|
refresh_dialogs_container!
|
|
527
|
-
push_dialogs_update! if @dialogs_container_mounted
|
|
528
593
|
end
|
|
529
594
|
|
|
530
595
|
def snack_bar=(value)
|
|
531
596
|
@snack_bar = value
|
|
532
597
|
refresh_dialogs_container!
|
|
533
|
-
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
def snack_bar
|
|
601
|
+
@snack_bar
|
|
534
602
|
end
|
|
535
603
|
|
|
536
604
|
def snackbar=(value)
|
|
@@ -542,6 +610,10 @@ module Ruflet
|
|
|
542
610
|
refresh_dialogs_container!
|
|
543
611
|
end
|
|
544
612
|
|
|
613
|
+
def bottom_sheet
|
|
614
|
+
@bottom_sheet
|
|
615
|
+
end
|
|
616
|
+
|
|
545
617
|
def bottomsheet=(value)
|
|
546
618
|
self.bottom_sheet = value
|
|
547
619
|
end
|
|
@@ -552,7 +624,6 @@ module Ruflet
|
|
|
552
624
|
return self if dialog_open?(dialog_control)
|
|
553
625
|
|
|
554
626
|
dialog_control.props["open"] = true
|
|
555
|
-
remove_existing_singleton_dialogs(dialog_control)
|
|
556
627
|
@dialogs << dialog_control unless @dialogs.include?(dialog_control)
|
|
557
628
|
refresh_dialogs_container!
|
|
558
629
|
send_view_patch unless @dialogs_container.wire_id
|
|
@@ -560,6 +631,72 @@ module Ruflet
|
|
|
560
631
|
self
|
|
561
632
|
end
|
|
562
633
|
|
|
634
|
+
def show_snack_bar(snack_bar_control)
|
|
635
|
+
show_dialog(snack_bar_control)
|
|
636
|
+
end
|
|
637
|
+
|
|
638
|
+
def show_snackbar(snack_bar_control)
|
|
639
|
+
show_dialog(snack_bar_control)
|
|
640
|
+
end
|
|
641
|
+
|
|
642
|
+
def show_bottom_sheet(bottom_sheet_control)
|
|
643
|
+
show_dialog(bottom_sheet_control)
|
|
644
|
+
end
|
|
645
|
+
|
|
646
|
+
def show_banner(banner_control)
|
|
647
|
+
show_dialog(banner_control)
|
|
648
|
+
end
|
|
649
|
+
|
|
650
|
+
def close_banner(banner_control = nil)
|
|
651
|
+
close_dialog(banner_control)
|
|
652
|
+
end
|
|
653
|
+
|
|
654
|
+
def close_dialog(dialog_control = nil)
|
|
655
|
+
target = dialog_control || latest_open_dialog
|
|
656
|
+
return nil unless target
|
|
657
|
+
|
|
658
|
+
target.props["open"] = false
|
|
659
|
+
refresh_dialogs_container!
|
|
660
|
+
push_dialogs_update!
|
|
661
|
+
target
|
|
662
|
+
end
|
|
663
|
+
|
|
664
|
+
def show_drawer(timeout: 10, on_result: nil)
|
|
665
|
+
raise ArgumentError, "No drawer defined" unless drawer
|
|
666
|
+
|
|
667
|
+
invoke(:page, "show_drawer", timeout: timeout, on_result: on_result)
|
|
668
|
+
end
|
|
669
|
+
|
|
670
|
+
def close_drawer(timeout: 10, on_result: nil)
|
|
671
|
+
invoke(:page, "close_drawer", timeout: timeout, on_result: on_result)
|
|
672
|
+
end
|
|
673
|
+
|
|
674
|
+
def show_end_drawer(timeout: 10, on_result: nil)
|
|
675
|
+
raise ArgumentError, "No end_drawer defined" unless end_drawer
|
|
676
|
+
|
|
677
|
+
invoke(:page, "show_end_drawer", timeout: timeout, on_result: on_result)
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
def close_end_drawer(timeout: 10, on_result: nil)
|
|
681
|
+
invoke(:page, "close_end_drawer", timeout: timeout, on_result: on_result)
|
|
682
|
+
end
|
|
683
|
+
|
|
684
|
+
def scroll_to(offset: nil, delta: nil, scroll_key: nil, duration: nil, curve: nil, timeout: 10, on_result: nil)
|
|
685
|
+
invoke(
|
|
686
|
+
:page,
|
|
687
|
+
"scroll_to",
|
|
688
|
+
args: {
|
|
689
|
+
"offset" => offset,
|
|
690
|
+
"delta" => delta,
|
|
691
|
+
"scroll_key" => scroll_key,
|
|
692
|
+
"duration" => duration,
|
|
693
|
+
"curve" => curve
|
|
694
|
+
},
|
|
695
|
+
timeout: timeout,
|
|
696
|
+
on_result: on_result
|
|
697
|
+
)
|
|
698
|
+
end
|
|
699
|
+
|
|
563
700
|
def invoke(control_or_id, method_name, args: nil, timeout: 10, on_result: nil)
|
|
564
701
|
control_id =
|
|
565
702
|
if page_control_target?(control_or_id)
|
|
@@ -573,7 +710,7 @@ module Ruflet
|
|
|
573
710
|
call_id = "call_#{Ruflet::Control.generate_id}"
|
|
574
711
|
if on_result.respond_to?(:call)
|
|
575
712
|
@invoke_waiters_mutex.synchronize { @invoke_callbacks[call_id] = on_result }
|
|
576
|
-
|
|
713
|
+
unless timeout.nil?
|
|
577
714
|
Thread.new(call_id, timeout.to_f) do |pending_call_id, invoke_timeout|
|
|
578
715
|
sleep([invoke_timeout, 0.0].max + 0.1)
|
|
579
716
|
callback = @invoke_waiters_mutex.synchronize { @invoke_callbacks.delete(pending_call_id) }
|
|
@@ -722,167 +859,6 @@ module Ruflet
|
|
|
722
859
|
upload(files, timeout: timeout, on_result: on_result)
|
|
723
860
|
end
|
|
724
861
|
|
|
725
|
-
def disable_browser_context_menu(timeout: 10, on_result: nil)
|
|
726
|
-
invoke_browser_context_menu("disable_menu", timeout: timeout, on_result: on_result)
|
|
727
|
-
end
|
|
728
|
-
|
|
729
|
-
def enable_browser_context_menu(timeout: 10, on_result: nil)
|
|
730
|
-
invoke_browser_context_menu("enable_menu", timeout: timeout, on_result: on_result)
|
|
731
|
-
end
|
|
732
|
-
|
|
733
|
-
def wait_until_ready_to_show(timeout: 10, on_result: nil)
|
|
734
|
-
invoke_window("wait_until_ready_to_show", timeout: timeout, on_result: on_result)
|
|
735
|
-
end
|
|
736
|
-
|
|
737
|
-
def window_to_front(timeout: 10, on_result: nil)
|
|
738
|
-
invoke_window("to_front", timeout: timeout, on_result: on_result)
|
|
739
|
-
end
|
|
740
|
-
|
|
741
|
-
def center_window(timeout: 10, on_result: nil)
|
|
742
|
-
invoke_window("center", timeout: timeout, on_result: on_result)
|
|
743
|
-
end
|
|
744
|
-
|
|
745
|
-
def close_window(timeout: 10, on_result: nil)
|
|
746
|
-
invoke_window("close", timeout: timeout, on_result: on_result)
|
|
747
|
-
end
|
|
748
|
-
|
|
749
|
-
def destroy_window(timeout: 10, on_result: nil)
|
|
750
|
-
invoke_window("destroy", timeout: timeout, on_result: on_result)
|
|
751
|
-
end
|
|
752
|
-
|
|
753
|
-
def start_window_dragging(timeout: 10, on_result: nil)
|
|
754
|
-
invoke_window("start_dragging", timeout: timeout, on_result: on_result)
|
|
755
|
-
end
|
|
756
|
-
|
|
757
|
-
def start_window_resizing(edge, timeout: 10, on_result: nil)
|
|
758
|
-
invoke_window(
|
|
759
|
-
"start_resizing",
|
|
760
|
-
args: { "edge" => normalize_service_value(edge) },
|
|
761
|
-
timeout: timeout,
|
|
762
|
-
on_result: on_result
|
|
763
|
-
)
|
|
764
|
-
end
|
|
765
|
-
|
|
766
|
-
def tester_pump(options = nil, duration: nil, timeout: 10, on_result: nil)
|
|
767
|
-
duration = options[:duration] || options["duration"] if options.is_a?(Hash) && duration.nil?
|
|
768
|
-
invoke_tester("pump", args: compact_service_args("duration" => duration), timeout: timeout, on_result: on_result)
|
|
769
|
-
end
|
|
770
|
-
|
|
771
|
-
def tester_pump_and_settle(options = nil, duration: nil, timeout: 10, on_result: nil)
|
|
772
|
-
duration = options[:duration] || options["duration"] if options.is_a?(Hash) && duration.nil?
|
|
773
|
-
invoke_tester("pump_and_settle", args: compact_service_args("duration" => duration), timeout: timeout, on_result: on_result)
|
|
774
|
-
end
|
|
775
|
-
|
|
776
|
-
def find_by_text(text, timeout: 10, on_result: nil)
|
|
777
|
-
invoke_tester("find_by_text", args: { "text" => text }, timeout: timeout, on_result: on_result)
|
|
778
|
-
end
|
|
779
|
-
|
|
780
|
-
def find_by_text_containing(pattern, timeout: 10, on_result: nil)
|
|
781
|
-
invoke_tester("find_by_text_containing", args: { "pattern" => pattern }, timeout: timeout, on_result: on_result)
|
|
782
|
-
end
|
|
783
|
-
|
|
784
|
-
def find_by_key(key, timeout: 10, on_result: nil)
|
|
785
|
-
invoke_tester("find_by_key", args: { "key" => key }, timeout: timeout, on_result: on_result)
|
|
786
|
-
end
|
|
787
|
-
|
|
788
|
-
def find_by_tooltip(value, timeout: 10, on_result: nil)
|
|
789
|
-
invoke_tester("find_by_tooltip", args: { "value" => value }, timeout: timeout, on_result: on_result)
|
|
790
|
-
end
|
|
791
|
-
|
|
792
|
-
def find_by_icon(icon, timeout: 10, on_result: nil)
|
|
793
|
-
invoke_tester("find_by_icon", args: { "icon" => normalize_service_value(icon) }, timeout: timeout, on_result: on_result)
|
|
794
|
-
end
|
|
795
|
-
|
|
796
|
-
def take_screenshot(name, timeout: 10, on_result: nil)
|
|
797
|
-
invoke_tester("take_screenshot", args: { "name" => name }, timeout: timeout, on_result: on_result)
|
|
798
|
-
end
|
|
799
|
-
|
|
800
|
-
def tap(finder_id = nil, options = nil, finder_index: nil, timeout: 10, on_result: nil)
|
|
801
|
-
finder_index = options[:finder_index] || options["finder_index"] if options.is_a?(Hash) && finder_index.nil?
|
|
802
|
-
invoke_tester_finder("tap", finder_id, finder_index: finder_index, timeout: timeout, on_result: on_result)
|
|
803
|
-
end
|
|
804
|
-
|
|
805
|
-
def mouse_click(finder_id = nil, options = nil, finder_index: nil, timeout: 10, on_result: nil)
|
|
806
|
-
finder_index = options[:finder_index] || options["finder_index"] if options.is_a?(Hash) && finder_index.nil?
|
|
807
|
-
invoke_tester_finder("mouse_click", finder_id, finder_index: finder_index, timeout: timeout, on_result: on_result)
|
|
808
|
-
end
|
|
809
|
-
|
|
810
|
-
def mouse_double_click(finder_id = nil, options = nil, finder_index: nil, timeout: 10, on_result: nil)
|
|
811
|
-
finder_index = options[:finder_index] || options["finder_index"] if options.is_a?(Hash) && finder_index.nil?
|
|
812
|
-
invoke_tester_finder("mouse_double_click", finder_id, finder_index: finder_index, timeout: timeout, on_result: on_result)
|
|
813
|
-
end
|
|
814
|
-
|
|
815
|
-
def right_mouse_click(finder_id = nil, options = nil, finder_index: nil, timeout: 10, on_result: nil)
|
|
816
|
-
finder_index = options[:finder_index] || options["finder_index"] if options.is_a?(Hash) && finder_index.nil?
|
|
817
|
-
invoke_tester_finder("right_mouse_click", finder_id, finder_index: finder_index, timeout: timeout, on_result: on_result)
|
|
818
|
-
end
|
|
819
|
-
|
|
820
|
-
def tap_at(offset = nil, timeout: 10, on_result: nil)
|
|
821
|
-
invoke_tester_at("tap_at", offset, timeout: timeout, on_result: on_result)
|
|
822
|
-
end
|
|
823
|
-
|
|
824
|
-
def mouse_click_at(offset = nil, timeout: 10, on_result: nil)
|
|
825
|
-
invoke_tester_at("mouse_click_at", offset, timeout: timeout, on_result: on_result)
|
|
826
|
-
end
|
|
827
|
-
|
|
828
|
-
def mouse_double_click_at(offset = nil, timeout: 10, on_result: nil)
|
|
829
|
-
invoke_tester_at("mouse_double_click_at", offset, timeout: timeout, on_result: on_result)
|
|
830
|
-
end
|
|
831
|
-
|
|
832
|
-
def right_mouse_click_at(offset = nil, timeout: 10, on_result: nil)
|
|
833
|
-
invoke_tester_at("right_mouse_click_at", offset, timeout: timeout, on_result: on_result)
|
|
834
|
-
end
|
|
835
|
-
|
|
836
|
-
def drag(finder_id, offset, finder_index: nil, timeout: 10, on_result: nil)
|
|
837
|
-
invoke_tester(
|
|
838
|
-
"drag",
|
|
839
|
-
args: compact_service_args(
|
|
840
|
-
"finder_id" => finder_id,
|
|
841
|
-
"finder_index" => finder_index,
|
|
842
|
-
"offset" => offset
|
|
843
|
-
),
|
|
844
|
-
timeout: timeout,
|
|
845
|
-
on_result: on_result
|
|
846
|
-
)
|
|
847
|
-
end
|
|
848
|
-
|
|
849
|
-
def drag_from(start, offset, timeout: 10, on_result: nil)
|
|
850
|
-
invoke_tester(
|
|
851
|
-
"drag_from",
|
|
852
|
-
args: compact_service_args("start" => start, "offset" => offset),
|
|
853
|
-
timeout: timeout,
|
|
854
|
-
on_result: on_result
|
|
855
|
-
)
|
|
856
|
-
end
|
|
857
|
-
|
|
858
|
-
def long_press(finder_id = nil, options = nil, finder_index: nil, timeout: 10, on_result: nil)
|
|
859
|
-
finder_index = options[:finder_index] || options["finder_index"] if options.is_a?(Hash) && finder_index.nil?
|
|
860
|
-
invoke_tester_finder("long_press", finder_id, finder_index: finder_index, timeout: timeout, on_result: on_result)
|
|
861
|
-
end
|
|
862
|
-
|
|
863
|
-
def enter_text(finder_id, text, options = nil, finder_index: nil, timeout: 10, on_result: nil)
|
|
864
|
-
finder_index = options[:finder_index] || options["finder_index"] if options.is_a?(Hash) && finder_index.nil?
|
|
865
|
-
invoke_tester(
|
|
866
|
-
"enter_text",
|
|
867
|
-
args: compact_service_args(
|
|
868
|
-
"finder_id" => finder_id,
|
|
869
|
-
"finder_index" => finder_index,
|
|
870
|
-
"text" => text
|
|
871
|
-
),
|
|
872
|
-
timeout: timeout,
|
|
873
|
-
on_result: on_result
|
|
874
|
-
)
|
|
875
|
-
end
|
|
876
|
-
|
|
877
|
-
def mouse_hover(finder_id = nil, options = nil, finder_index: nil, timeout: 10, on_result: nil)
|
|
878
|
-
finder_index = options[:finder_index] || options["finder_index"] if options.is_a?(Hash) && finder_index.nil?
|
|
879
|
-
invoke_tester_finder("mouse_hover", finder_id, finder_index: finder_index, timeout: timeout, on_result: on_result)
|
|
880
|
-
end
|
|
881
|
-
|
|
882
|
-
def tester_teardown(timeout: 10, on_result: nil)
|
|
883
|
-
invoke_tester("teardown", timeout: timeout, on_result: on_result)
|
|
884
|
-
end
|
|
885
|
-
|
|
886
862
|
def heavy_impact(timeout: 10, on_result: nil)
|
|
887
863
|
invoke_haptic_feedback("heavy_impact", timeout: timeout, on_result: on_result)
|
|
888
864
|
end
|
|
@@ -982,6 +958,18 @@ module Ruflet
|
|
|
982
958
|
service(:barometer, **props)
|
|
983
959
|
end
|
|
984
960
|
|
|
961
|
+
def geolocator(**props)
|
|
962
|
+
service(:geolocator, **props)
|
|
963
|
+
end
|
|
964
|
+
|
|
965
|
+
def permission_handler(**props)
|
|
966
|
+
service(:permission_handler, **props)
|
|
967
|
+
end
|
|
968
|
+
|
|
969
|
+
def secure_storage(**props)
|
|
970
|
+
service(:secure_storage, **props)
|
|
971
|
+
end
|
|
972
|
+
|
|
985
973
|
def shake_detector(**props)
|
|
986
974
|
service(:shake_detector, **props)
|
|
987
975
|
end
|
|
@@ -991,7 +979,7 @@ module Ruflet
|
|
|
991
979
|
end
|
|
992
980
|
|
|
993
981
|
def screenshot(**props)
|
|
994
|
-
|
|
982
|
+
Ruflet::UI::ControlFactory.build(:screenshot, **props)
|
|
995
983
|
end
|
|
996
984
|
|
|
997
985
|
def battery(**props)
|
|
@@ -1030,18 +1018,6 @@ module Ruflet
|
|
|
1030
1018
|
service(:haptic_feedback, **props)
|
|
1031
1019
|
end
|
|
1032
1020
|
|
|
1033
|
-
def geolocator(**props)
|
|
1034
|
-
service(:geolocator, **props)
|
|
1035
|
-
end
|
|
1036
|
-
|
|
1037
|
-
def permission_handler(**props)
|
|
1038
|
-
service(:permission_handler, **props)
|
|
1039
|
-
end
|
|
1040
|
-
|
|
1041
|
-
def secure_storage(**props)
|
|
1042
|
-
service(:secure_storage, **props)
|
|
1043
|
-
end
|
|
1044
|
-
|
|
1045
1021
|
def get_application_cache_directory(timeout: nil, on_result: nil)
|
|
1046
1022
|
invoke_storage_paths("get_application_cache_directory", timeout: timeout, on_result: on_result)
|
|
1047
1023
|
end
|
|
@@ -1186,27 +1162,7 @@ module Ruflet
|
|
|
1186
1162
|
end
|
|
1187
1163
|
|
|
1188
1164
|
def pop_dialog
|
|
1189
|
-
|
|
1190
|
-
return nil unless dialog_control
|
|
1191
|
-
|
|
1192
|
-
dialog_control.props["open"] = false
|
|
1193
|
-
update(dialog_control, open: false)
|
|
1194
|
-
dialog_control
|
|
1195
|
-
end
|
|
1196
|
-
|
|
1197
|
-
def close_dialog(dialog_control)
|
|
1198
|
-
return self unless dialog_control
|
|
1199
|
-
|
|
1200
|
-
dialog_control.props["open"] = false
|
|
1201
|
-
@dialog = nil if @dialog.equal?(dialog_control)
|
|
1202
|
-
remove_dialog_tracking(dialog_control)
|
|
1203
|
-
refresh_dialogs_container!
|
|
1204
|
-
# Patch the dialogs container in place. Forcing a full view re-render
|
|
1205
|
-
# here would remount the whole overlay — fatal while another dialog
|
|
1206
|
-
# (e.g. the form behind a nested picker) is still open. The empty case
|
|
1207
|
-
# is handled inside push_dialogs_update!.
|
|
1208
|
-
push_dialogs_update!
|
|
1209
|
-
self
|
|
1165
|
+
close_dialog
|
|
1210
1166
|
end
|
|
1211
1167
|
|
|
1212
1168
|
def update(control_or_id = nil, **props)
|
|
@@ -1244,7 +1200,6 @@ module Ruflet
|
|
|
1244
1200
|
|
|
1245
1201
|
visited = Set.new
|
|
1246
1202
|
patch.each_value { |value| register_embedded_value(value, visited) }
|
|
1247
|
-
patch.each { |k, v| control.props[k] = v }
|
|
1248
1203
|
|
|
1249
1204
|
patch_ops = patch.map { |k, v| [0, 0, k, serialize_patch_value(v)] }
|
|
1250
1205
|
|
|
@@ -1256,6 +1211,10 @@ module Ruflet
|
|
|
1256
1211
|
self
|
|
1257
1212
|
end
|
|
1258
1213
|
|
|
1214
|
+
def schedule_update
|
|
1215
|
+
update
|
|
1216
|
+
end
|
|
1217
|
+
|
|
1259
1218
|
def patch_page(control_id, **props)
|
|
1260
1219
|
update(control_id, **props)
|
|
1261
1220
|
end
|
|
@@ -1267,6 +1226,8 @@ module Ruflet
|
|
|
1267
1226
|
patch = normalize_props(props || {})
|
|
1268
1227
|
patch.each { |k, v| control.props[k] = v }
|
|
1269
1228
|
|
|
1229
|
+
remove_dialog_tracking(control) if patch.key?("open") && patch["open"] == false
|
|
1230
|
+
|
|
1270
1231
|
self
|
|
1271
1232
|
end
|
|
1272
1233
|
|
|
@@ -1274,17 +1235,8 @@ module Ruflet
|
|
|
1274
1235
|
if page_control_target?(target)
|
|
1275
1236
|
if name.to_s == "route_change"
|
|
1276
1237
|
route_from_event = extract_route(data)
|
|
1277
|
-
|
|
1278
|
-
# Navigating away must dismiss them, or they ghost onto the next
|
|
1279
|
-
# view — the picker that "reappears after going home".
|
|
1280
|
-
dismiss_tracked_dialogs! if route_from_event && route_from_event != @page_props["route"]
|
|
1238
|
+
return if route_from_event && route_from_event == @page_props["route"]
|
|
1281
1239
|
@page_props["route"] = route_from_event if route_from_event
|
|
1282
|
-
elsif name.to_s == "resize"
|
|
1283
|
-
# The client reports the live page size via the "resize" event. Store
|
|
1284
|
-
# it so `page.width`/`page.height` reflect the real viewport — without
|
|
1285
|
-
# this, responsive layouts collapse on clients (e.g. embedded/iOS)
|
|
1286
|
-
# that don't know their size at the initial handshake.
|
|
1287
|
-
store_reported_page_size(data)
|
|
1288
1240
|
end
|
|
1289
1241
|
dispatch_page_event(name: name, data: data)
|
|
1290
1242
|
return
|
|
@@ -1293,19 +1245,13 @@ module Ruflet
|
|
|
1293
1245
|
control = @wire_index[target.to_i] || @control_index[target.to_s]
|
|
1294
1246
|
return unless control
|
|
1295
1247
|
|
|
1296
|
-
event =
|
|
1248
|
+
event = Event.new(name: name, target: target, raw_data: data, page: self, control: control)
|
|
1297
1249
|
apply_event_value_to_control(control, event) if %w[change select select_change].include?(name.to_s)
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
mark_picker_dialog_closed(control, name)
|
|
1302
|
-
if dialog_close_event?(control, name) && remove_dialog_tracking(control)
|
|
1303
|
-
# Patch the container in place; never force a full view re-render that
|
|
1304
|
-
# would remount a still-open parent dialog (the nested-picker case).
|
|
1250
|
+
control.emit(name, event)
|
|
1251
|
+
|
|
1252
|
+
if name.to_s == "dismiss" && remove_dialog_tracking(control)
|
|
1305
1253
|
push_dialogs_update!
|
|
1306
1254
|
end
|
|
1307
|
-
|
|
1308
|
-
control.emit(name, event)
|
|
1309
1255
|
end
|
|
1310
1256
|
|
|
1311
1257
|
def method_missing(name, *args, &block)
|
|
@@ -1323,6 +1269,9 @@ module Ruflet
|
|
|
1323
1269
|
if args.empty? && !block
|
|
1324
1270
|
return @page_props[method_name] if @page_props.key?(method_name)
|
|
1325
1271
|
return @view_props[method_name] if @view_props.key?(method_name)
|
|
1272
|
+
# Client-reported page properties (width, height, platform,
|
|
1273
|
+
# platform_brightness, media) arrive in the register payload.
|
|
1274
|
+
return @client_details[method_name] if @client_details.respond_to?(:key?) && @client_details.key?(method_name)
|
|
1326
1275
|
return instance_variable_get("@#{method_name}") if DIALOG_PROP_KEYS.include?(method_name)
|
|
1327
1276
|
end
|
|
1328
1277
|
|
|
@@ -1347,14 +1296,20 @@ module Ruflet
|
|
|
1347
1296
|
|
|
1348
1297
|
private
|
|
1349
1298
|
|
|
1350
|
-
def
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1299
|
+
def build_client_window(snapshot)
|
|
1300
|
+
properties = snapshot.is_a?(Hash) ? snapshot : {}
|
|
1301
|
+
allowed = Ruflet::UI::Controls::RufletComponents::WindowControl::KEYWORDS
|
|
1302
|
+
props = properties.each_with_object({}) do |(key, value), result|
|
|
1303
|
+
name = key.to_s
|
|
1304
|
+
result[name.to_sym] = value if allowed.include?(name.to_sym)
|
|
1305
|
+
end
|
|
1306
|
+
control = Ruflet::UI::Controls::RufletComponents::WindowControl.new(
|
|
1307
|
+
id: "_window",
|
|
1308
|
+
**props
|
|
1309
|
+
)
|
|
1310
|
+
control.wire_id = (properties["_i"] || properties[:_i] || 2).to_i
|
|
1311
|
+
control.runtime_page = self
|
|
1312
|
+
control
|
|
1358
1313
|
end
|
|
1359
1314
|
|
|
1360
1315
|
def invoke_and_wait(control_or_id, method_name, args: nil, timeout: 10)
|
|
@@ -1440,6 +1395,14 @@ module Ruflet
|
|
|
1440
1395
|
type.to_s.delete("_") == "camera"
|
|
1441
1396
|
end
|
|
1442
1397
|
|
|
1398
|
+
def service_control_type?(type)
|
|
1399
|
+
normalized = type.to_s.downcase
|
|
1400
|
+
compact = normalized.delete("_")
|
|
1401
|
+
compact == "audio" ||
|
|
1402
|
+
Ruflet::UI::Services::RufletServices::CLASS_MAP.key?(normalized) ||
|
|
1403
|
+
Ruflet::UI::Services::RufletServices::CLASS_MAP.key?(compact)
|
|
1404
|
+
end
|
|
1405
|
+
|
|
1443
1406
|
def text_maps_to_content?(control, patch)
|
|
1444
1407
|
patch.key?("text") && control.type.end_with?("button")
|
|
1445
1408
|
end
|
|
@@ -1454,12 +1417,14 @@ module Ruflet
|
|
|
1454
1417
|
@sender.call(action, payload)
|
|
1455
1418
|
end
|
|
1456
1419
|
|
|
1457
|
-
def
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1420
|
+
def replace_root_controls(controls)
|
|
1421
|
+
visited = Set.new
|
|
1422
|
+
controls.each { |control| register_control_tree(control, visited) }
|
|
1423
|
+
@root_controls = controls
|
|
1424
|
+
|
|
1425
|
+
refresh_dialogs_container!
|
|
1426
|
+
@view_props.each_value { |value| register_embedded_value(value, visited) }
|
|
1427
|
+
send_view_patch
|
|
1463
1428
|
end
|
|
1464
1429
|
|
|
1465
1430
|
def send_view_patch
|
|
@@ -1471,8 +1436,8 @@ module Ruflet
|
|
|
1471
1436
|
"id" => 1,
|
|
1472
1437
|
"patch" => [
|
|
1473
1438
|
[0],
|
|
1474
|
-
|
|
1475
|
-
|
|
1439
|
+
[0, 0, "views", view_patches],
|
|
1440
|
+
*page_patch_ops
|
|
1476
1441
|
]
|
|
1477
1442
|
})
|
|
1478
1443
|
@overlay_container_mounted = true if @overlay_container.wire_id
|
|
@@ -1592,7 +1557,8 @@ module Ruflet
|
|
|
1592
1557
|
query_string = route_value.to_s.split("?", 2)[1].to_s
|
|
1593
1558
|
return {} if query_string.empty?
|
|
1594
1559
|
|
|
1595
|
-
|
|
1560
|
+
URI.decode_www_form(query_string).group_by(&:first).each_with_object({}) do |(key, pairs), result|
|
|
1561
|
+
values = pairs.map(&:last)
|
|
1596
1562
|
result[key] = values.size == 1 ? values.first : values
|
|
1597
1563
|
end
|
|
1598
1564
|
end
|
|
@@ -1608,20 +1574,13 @@ module Ruflet
|
|
|
1608
1574
|
end
|
|
1609
1575
|
end
|
|
1610
1576
|
|
|
1611
|
-
def store_reported_page_size(data)
|
|
1612
|
-
return unless data.is_a?(Hash)
|
|
1613
|
-
|
|
1614
|
-
width = data["width"] || data[:width]
|
|
1615
|
-
height = data["height"] || data[:height]
|
|
1616
|
-
@page_props["width"] = width unless width.nil?
|
|
1617
|
-
@page_props["height"] = height unless height.nil?
|
|
1618
|
-
end
|
|
1619
|
-
|
|
1620
1577
|
def dispatch_page_event(name:, data:)
|
|
1621
|
-
|
|
1578
|
+
event_name = name.to_s
|
|
1579
|
+
event_name = event_name[3..-1] if event_name.start_with?("on_")
|
|
1580
|
+
handler = @page_event_handlers[event_name]
|
|
1622
1581
|
return unless handler.respond_to?(:call)
|
|
1623
1582
|
|
|
1624
|
-
event =
|
|
1583
|
+
event = Event.new(name: name.to_s, target: 1, raw_data: data, page: self, control: nil)
|
|
1625
1584
|
handler.call(event)
|
|
1626
1585
|
end
|
|
1627
1586
|
|
|
@@ -1637,8 +1596,8 @@ module Ruflet
|
|
|
1637
1596
|
end_value = range_value["end_value"] || range_value[:end_value]
|
|
1638
1597
|
control.props["start_value"] = start_value unless start_value.nil?
|
|
1639
1598
|
control.props["end_value"] = end_value unless end_value.nil?
|
|
1599
|
+
return
|
|
1640
1600
|
end
|
|
1641
|
-
return
|
|
1642
1601
|
end
|
|
1643
1602
|
|
|
1644
1603
|
return if value.nil?
|
|
@@ -1685,7 +1644,7 @@ module Ruflet
|
|
|
1685
1644
|
end
|
|
1686
1645
|
|
|
1687
1646
|
def refresh_dialogs_container!
|
|
1688
|
-
dialog_controls = (
|
|
1647
|
+
dialog_controls = (@dialogs + dialog_slots).uniq
|
|
1689
1648
|
@dialogs_container.props["controls"] = dialog_controls
|
|
1690
1649
|
@page_props["_dialogs"] = @dialogs_container
|
|
1691
1650
|
end
|
|
@@ -1694,6 +1653,19 @@ module Ruflet
|
|
|
1694
1653
|
@page_props["_overlay"] = @overlay_container
|
|
1695
1654
|
end
|
|
1696
1655
|
|
|
1656
|
+
def push_overlay_update!
|
|
1657
|
+
refresh_control_indexes!
|
|
1658
|
+
|
|
1659
|
+
if @overlay_container.wire_id
|
|
1660
|
+
send_message(Protocol::ACTIONS[:patch_control], {
|
|
1661
|
+
"id" => @overlay_container.wire_id,
|
|
1662
|
+
"patch" => [[0], [0, 0, "controls", serialize_patch_value(@overlay_container.children)]]
|
|
1663
|
+
})
|
|
1664
|
+
else
|
|
1665
|
+
send_view_patch
|
|
1666
|
+
end
|
|
1667
|
+
end
|
|
1668
|
+
|
|
1697
1669
|
def refresh_services_container!
|
|
1698
1670
|
@page_props["_services"] = @services_container
|
|
1699
1671
|
end
|
|
@@ -1714,12 +1686,6 @@ module Ruflet
|
|
|
1714
1686
|
def push_dialogs_update!
|
|
1715
1687
|
refresh_control_indexes!
|
|
1716
1688
|
|
|
1717
|
-
# Once the dialogs container is mounted, every change — opening, closing,
|
|
1718
|
-
# even down to no dialogs at all — is an in-place patch of its controls
|
|
1719
|
-
# list. Re-sending the view (or the container as a whole object) would
|
|
1720
|
-
# replace the live container instance on the Flutter side, detaching its
|
|
1721
|
-
# listeners and breaking any other dialog still open. Only the very first
|
|
1722
|
-
# dialog, before the container has a wire id, needs a view patch to mount.
|
|
1723
1689
|
if @dialogs_container.wire_id
|
|
1724
1690
|
send_message(Protocol::ACTIONS[:patch_control], {
|
|
1725
1691
|
"id" => @dialogs_container.wire_id,
|
|
@@ -1742,41 +1708,6 @@ module Ruflet
|
|
|
1742
1708
|
@dialogs.include?(dialog_control) && dialog_control.props["open"] == true
|
|
1743
1709
|
end
|
|
1744
1710
|
|
|
1745
|
-
def dialog_close_event?(control, name)
|
|
1746
|
-
name = name.to_s
|
|
1747
|
-
name == "dismiss" || (%w[change select select_change].include?(name) && @dialogs.include?(control) && control.props["open"] == false)
|
|
1748
|
-
end
|
|
1749
|
-
|
|
1750
|
-
# Picker dialogs that auto-dismiss on the client after a selection. Their
|
|
1751
|
-
# confirm sends a value event (change/select), not a close, so the server
|
|
1752
|
-
# must flip `open` to false or show_dialog's open-guard blocks reopening.
|
|
1753
|
-
PICKER_DIALOG_TYPES = %w[
|
|
1754
|
-
datepicker daterangepicker timepicker
|
|
1755
|
-
cupertinodatepicker cupertinotimerpicker
|
|
1756
|
-
].freeze
|
|
1757
|
-
|
|
1758
|
-
def picker_dialog?(control)
|
|
1759
|
-
PICKER_DIALOG_TYPES.include?(control.type.to_s.tr("_", "").downcase)
|
|
1760
|
-
end
|
|
1761
|
-
|
|
1762
|
-
def mark_picker_dialog_closed(control, name)
|
|
1763
|
-
return unless picker_dialog?(control)
|
|
1764
|
-
return unless %w[change select select_change dismiss].include?(name.to_s)
|
|
1765
|
-
|
|
1766
|
-
control.props["open"] = false
|
|
1767
|
-
end
|
|
1768
|
-
|
|
1769
|
-
# Close and untrack every dialog currently shown. Called on navigation so
|
|
1770
|
-
# a dialog opened in one view does not linger as an overlay on the next.
|
|
1771
|
-
def dismiss_tracked_dialogs!
|
|
1772
|
-
return if @dialogs.empty?
|
|
1773
|
-
|
|
1774
|
-
@dialogs.each { |dialog| dialog.props["open"] = false }
|
|
1775
|
-
@dialogs.clear
|
|
1776
|
-
refresh_dialogs_container!
|
|
1777
|
-
push_dialogs_update! if @dialogs_container_mounted
|
|
1778
|
-
end
|
|
1779
|
-
|
|
1780
1711
|
def remove_dialog_tracking(control)
|
|
1781
1712
|
return false unless @dialogs.include?(control)
|
|
1782
1713
|
|
|
@@ -1785,16 +1716,6 @@ module Ruflet
|
|
|
1785
1716
|
true
|
|
1786
1717
|
end
|
|
1787
1718
|
|
|
1788
|
-
def remove_existing_singleton_dialogs(control)
|
|
1789
|
-
return unless singleton_dialog_control?(control)
|
|
1790
|
-
|
|
1791
|
-
@dialogs.delete_if { |dialog| dialog != control && singleton_dialog_control?(dialog) }
|
|
1792
|
-
end
|
|
1793
|
-
|
|
1794
|
-
def singleton_dialog_control?(control)
|
|
1795
|
-
control.type.to_s.tr("_", "").downcase == "snackbar"
|
|
1796
|
-
end
|
|
1797
|
-
|
|
1798
1719
|
def assign_split_prop(key, value)
|
|
1799
1720
|
if key == "vertical_alignment" || key == "horizontal_alignment"
|
|
1800
1721
|
@page_props[key] = value
|
|
@@ -1841,7 +1762,6 @@ module Ruflet
|
|
|
1841
1762
|
existing = service_by_type(type)
|
|
1842
1763
|
return [existing, false] if existing
|
|
1843
1764
|
|
|
1844
|
-
# `service` already syncs via add_service -> push_services_update!.
|
|
1845
1765
|
[service(type), true]
|
|
1846
1766
|
end
|
|
1847
1767
|
|
|
@@ -1866,51 +1786,6 @@ module Ruflet
|
|
|
1866
1786
|
service(:url_launcher)
|
|
1867
1787
|
end
|
|
1868
1788
|
|
|
1869
|
-
def ensure_browser_context_menu_service
|
|
1870
|
-
service(:browser_context_menu)
|
|
1871
|
-
end
|
|
1872
|
-
|
|
1873
|
-
def invoke_browser_context_menu(method_name, timeout:, on_result:)
|
|
1874
|
-
browser_context_menu = ensure_browser_context_menu_service
|
|
1875
|
-
invoke(browser_context_menu, method_name, timeout: timeout, on_result: on_result)
|
|
1876
|
-
end
|
|
1877
|
-
|
|
1878
|
-
def ensure_window_service
|
|
1879
|
-
service(:window)
|
|
1880
|
-
end
|
|
1881
|
-
|
|
1882
|
-
def invoke_window(method_name, args: nil, timeout:, on_result:)
|
|
1883
|
-
window = ensure_window_service
|
|
1884
|
-
invoke(window, method_name, args: args, timeout: timeout, on_result: on_result)
|
|
1885
|
-
end
|
|
1886
|
-
|
|
1887
|
-
def ensure_tester_service
|
|
1888
|
-
service(:tester)
|
|
1889
|
-
end
|
|
1890
|
-
|
|
1891
|
-
def invoke_tester(method_name, args: nil, timeout:, on_result:)
|
|
1892
|
-
tester = ensure_tester_service
|
|
1893
|
-
invoke(tester, method_name, args: args, timeout: timeout, on_result: on_result)
|
|
1894
|
-
end
|
|
1895
|
-
|
|
1896
|
-
def invoke_tester_finder(method_name, finder_id, finder_index:, timeout:, on_result:)
|
|
1897
|
-
invoke_tester(
|
|
1898
|
-
method_name,
|
|
1899
|
-
args: compact_service_args("finder_id" => finder_id, "finder_index" => finder_index),
|
|
1900
|
-
timeout: timeout,
|
|
1901
|
-
on_result: on_result
|
|
1902
|
-
)
|
|
1903
|
-
end
|
|
1904
|
-
|
|
1905
|
-
def invoke_tester_at(method_name, offset, timeout:, on_result:)
|
|
1906
|
-
invoke_tester(
|
|
1907
|
-
method_name,
|
|
1908
|
-
args: compact_service_args("offset" => offset),
|
|
1909
|
-
timeout: timeout,
|
|
1910
|
-
on_result: on_result
|
|
1911
|
-
)
|
|
1912
|
-
end
|
|
1913
|
-
|
|
1914
1789
|
def ensure_haptic_feedback_service
|
|
1915
1790
|
service(:haptic_feedback)
|
|
1916
1791
|
end
|
|
@@ -1920,38 +1795,6 @@ module Ruflet
|
|
|
1920
1795
|
invoke(haptic_feedback, method_name, timeout: timeout, on_result: on_result)
|
|
1921
1796
|
end
|
|
1922
1797
|
|
|
1923
|
-
def invoke_current_view(method_name, timeout:, on_result:)
|
|
1924
|
-
target_id = @views.last&.wire_id || @view_id
|
|
1925
|
-
invoke_control_id(target_id, method_name, timeout: timeout, on_result: on_result)
|
|
1926
|
-
end
|
|
1927
|
-
|
|
1928
|
-
def invoke_control_id(control_id, method_name, args: nil, timeout: 10, on_result: nil)
|
|
1929
|
-
call_id = "call_#{Ruflet::Control.generate_id}"
|
|
1930
|
-
if on_result.respond_to?(:call)
|
|
1931
|
-
@invoke_waiters_mutex.synchronize { @invoke_callbacks[call_id] = on_result }
|
|
1932
|
-
if embedded_async_timeout_available? && !timeout.nil?
|
|
1933
|
-
Thread.new(call_id, timeout.to_f) do |pending_call_id, invoke_timeout|
|
|
1934
|
-
sleep([invoke_timeout, 0.0].max + 0.1)
|
|
1935
|
-
callback = @invoke_waiters_mutex.synchronize { @invoke_callbacks.delete(pending_call_id) }
|
|
1936
|
-
callback&.call(nil, "execution expired")
|
|
1937
|
-
rescue StandardError => e
|
|
1938
|
-
Kernel.warn("invoke timeout callback error: #{e.class}: #{e.message}")
|
|
1939
|
-
end
|
|
1940
|
-
end
|
|
1941
|
-
end
|
|
1942
|
-
|
|
1943
|
-
payload = {
|
|
1944
|
-
"control_id" => control_id,
|
|
1945
|
-
"call_id" => call_id,
|
|
1946
|
-
"name" => method_name.to_s,
|
|
1947
|
-
"args" => args
|
|
1948
|
-
}
|
|
1949
|
-
payload["timeout"] = timeout unless timeout.nil?
|
|
1950
|
-
send_message(Protocol::ACTIONS[:invoke_control_method], payload)
|
|
1951
|
-
|
|
1952
|
-
call_id
|
|
1953
|
-
end
|
|
1954
|
-
|
|
1955
1798
|
def ensure_connectivity_service
|
|
1956
1799
|
service_with_created(:connectivity)
|
|
1957
1800
|
end
|