ruflet_core 0.0.17 → 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 -30
- 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 -50
- data/lib/ruflet_ui/ruflet/control.rb +69 -64
- 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 +308 -526
- 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 +7 -8
- 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",
|
|
@@ -183,7 +196,6 @@ module Ruflet
|
|
|
183
196
|
id: "_dialogs",
|
|
184
197
|
controls: []
|
|
185
198
|
)
|
|
186
|
-
@mounted_services = []
|
|
187
199
|
@invoke_waiters = {}
|
|
188
200
|
@invoke_callbacks = {}
|
|
189
201
|
@invoke_waiters_mutex = Mutex.new
|
|
@@ -245,64 +257,64 @@ module Ruflet
|
|
|
245
257
|
@view_props["bgcolor"] = normalize_value("bgcolor", value)
|
|
246
258
|
end
|
|
247
259
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
def width
|
|
253
|
-
client_reported_prop("width")
|
|
254
|
-
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
|
|
255
264
|
|
|
256
|
-
|
|
257
|
-
client_reported_prop("height")
|
|
258
|
-
end
|
|
265
|
+
raise ArgumentError, "Page#add accepts exactly one control" unless control.is_a?(Control)
|
|
259
266
|
|
|
260
|
-
|
|
261
|
-
|
|
267
|
+
replace_root_controls(@root_controls + [control])
|
|
268
|
+
self
|
|
262
269
|
end
|
|
263
270
|
|
|
264
|
-
def
|
|
265
|
-
|
|
271
|
+
def controls
|
|
272
|
+
@root_controls
|
|
266
273
|
end
|
|
267
274
|
|
|
268
|
-
def
|
|
269
|
-
|
|
275
|
+
def controls=(value)
|
|
276
|
+
replace_root_controls(Array(value).flatten.compact)
|
|
277
|
+
self
|
|
270
278
|
end
|
|
271
279
|
|
|
272
|
-
def
|
|
273
|
-
|
|
280
|
+
def insert(at, *controls)
|
|
281
|
+
@root_controls.insert(at.to_i, *controls.flatten.compact)
|
|
282
|
+
send_view_patch
|
|
283
|
+
self
|
|
274
284
|
end
|
|
275
285
|
|
|
276
|
-
def
|
|
277
|
-
|
|
286
|
+
def remove(*controls)
|
|
287
|
+
controls.flatten.each { |control| @root_controls.delete(control) }
|
|
288
|
+
send_view_patch
|
|
289
|
+
self
|
|
278
290
|
end
|
|
279
291
|
|
|
280
|
-
def
|
|
281
|
-
|
|
292
|
+
def remove_at(index)
|
|
293
|
+
@root_controls.delete_at(index.to_i)
|
|
294
|
+
send_view_patch
|
|
295
|
+
self
|
|
282
296
|
end
|
|
283
297
|
|
|
284
|
-
def
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
@root_controls = controls
|
|
289
|
-
|
|
290
|
-
update_view_slot("appbar", appbar)
|
|
291
|
-
update_view_slot("bottom_appbar", bottom_appbar)
|
|
292
|
-
update_view_slot("floating_action_button", floating_action_button)
|
|
293
|
-
update_view_slot("navigation_bar", navigation_bar)
|
|
294
|
-
@dialog = dialog if dialog
|
|
295
|
-
@snack_bar = snack_bar if snack_bar
|
|
296
|
-
@bottom_sheet = bottom_sheet if bottom_sheet
|
|
297
|
-
|
|
298
|
-
refresh_dialogs_container!
|
|
299
|
-
@view_props.each_value { |value| register_embedded_value(value, visited) }
|
|
298
|
+
def clean
|
|
299
|
+
replace_root_controls([])
|
|
300
|
+
self
|
|
301
|
+
end
|
|
300
302
|
|
|
301
|
-
|
|
303
|
+
def overlay
|
|
304
|
+
@overlay_container.children
|
|
305
|
+
end
|
|
302
306
|
|
|
307
|
+
def overlay=(value)
|
|
308
|
+
@overlay_container.children.replace(Array(value).flatten.compact)
|
|
309
|
+
push_overlay_update!
|
|
303
310
|
self
|
|
304
311
|
end
|
|
305
312
|
|
|
313
|
+
def get_control(id)
|
|
314
|
+
refresh_control_indexes!
|
|
315
|
+
resolve_control(id)
|
|
316
|
+
end
|
|
317
|
+
|
|
306
318
|
def views=(value)
|
|
307
319
|
@views = Array(value).compact
|
|
308
320
|
self
|
|
@@ -351,18 +363,6 @@ module Ruflet
|
|
|
351
363
|
service(:audio_recorder, **props)
|
|
352
364
|
end
|
|
353
365
|
|
|
354
|
-
def browser_context_menu(**props)
|
|
355
|
-
service(:browser_context_menu, **props)
|
|
356
|
-
end
|
|
357
|
-
|
|
358
|
-
def window(**props)
|
|
359
|
-
service(:window, **props)
|
|
360
|
-
end
|
|
361
|
-
|
|
362
|
-
def tester(**props)
|
|
363
|
-
service(:tester, **props)
|
|
364
|
-
end
|
|
365
|
-
|
|
366
366
|
def add_service(*value)
|
|
367
367
|
@services_container.props["_services"] = services + value.flatten.compact
|
|
368
368
|
refresh_services_container!
|
|
@@ -407,6 +407,10 @@ module Ruflet
|
|
|
407
407
|
return svc
|
|
408
408
|
end
|
|
409
409
|
|
|
410
|
+
unless service_control_type?(normalized_type)
|
|
411
|
+
raise ArgumentError, "#{type} is a visual control, not a page service"
|
|
412
|
+
end
|
|
413
|
+
|
|
410
414
|
existing =
|
|
411
415
|
if id
|
|
412
416
|
services.find { |s| s.is_a?(Control) && s.id.to_s == id.to_s }
|
|
@@ -415,7 +419,14 @@ module Ruflet
|
|
|
415
419
|
s.is_a?(Control) && s.type.to_s.downcase.delete("_") == compact_type
|
|
416
420
|
end
|
|
417
421
|
end
|
|
418
|
-
|
|
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
|
|
419
430
|
|
|
420
431
|
svc = Ruflet::UI::ControlFactory.build(type.to_s, id: id&.to_s, **mapped_props)
|
|
421
432
|
add_service(svc) unless services.include?(svc)
|
|
@@ -449,25 +460,35 @@ module Ruflet
|
|
|
449
460
|
@page_event_handlers["view_pop"] = handler
|
|
450
461
|
end
|
|
451
462
|
|
|
452
|
-
def
|
|
453
|
-
@page_event_handlers["
|
|
463
|
+
def on_views_pop_until=(handler)
|
|
464
|
+
@page_event_handlers["views_pop_until"] = handler
|
|
454
465
|
end
|
|
455
466
|
|
|
456
467
|
def on(event_name, &block)
|
|
457
|
-
|
|
468
|
+
name = event_name.to_s
|
|
469
|
+
name = name[3..-1] if name.start_with?("on_")
|
|
470
|
+
@page_event_handlers[name] = block
|
|
458
471
|
self
|
|
459
472
|
end
|
|
460
473
|
|
|
461
474
|
def mount(&block)
|
|
462
475
|
builder = WidgetBuilder.new
|
|
463
476
|
builder.instance_eval(&block)
|
|
464
|
-
|
|
477
|
+
builder.children.each { |control| add(control) }
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
def appbar
|
|
481
|
+
@view_props["appbar"]
|
|
465
482
|
end
|
|
466
483
|
|
|
467
484
|
def appbar=(value)
|
|
468
485
|
@view_props["appbar"] = value
|
|
469
486
|
end
|
|
470
487
|
|
|
488
|
+
def bottom_appbar
|
|
489
|
+
@view_props["bottom_appbar"]
|
|
490
|
+
end
|
|
491
|
+
|
|
471
492
|
def bottom_appbar=(value)
|
|
472
493
|
@view_props["bottom_appbar"] = value
|
|
473
494
|
end
|
|
@@ -476,48 +497,92 @@ module Ruflet
|
|
|
476
497
|
self.bottom_appbar = value
|
|
477
498
|
end
|
|
478
499
|
|
|
500
|
+
def floating_action_button
|
|
501
|
+
@view_props["floating_action_button"]
|
|
502
|
+
end
|
|
503
|
+
|
|
479
504
|
def floating_action_button=(value)
|
|
480
505
|
@view_props["floating_action_button"] = value
|
|
481
506
|
end
|
|
482
507
|
|
|
483
|
-
def
|
|
484
|
-
@view_props["
|
|
508
|
+
def navigation_bar
|
|
509
|
+
@view_props["navigation_bar"]
|
|
485
510
|
end
|
|
486
511
|
|
|
487
|
-
def
|
|
488
|
-
@view_props["
|
|
512
|
+
def navigation_bar=(value)
|
|
513
|
+
@view_props["navigation_bar"] = value
|
|
489
514
|
end
|
|
490
515
|
|
|
491
|
-
def
|
|
492
|
-
@view_props["
|
|
516
|
+
def auto_scroll
|
|
517
|
+
@view_props["auto_scroll"]
|
|
493
518
|
end
|
|
494
519
|
|
|
495
|
-
def
|
|
496
|
-
@view_props["
|
|
520
|
+
def auto_scroll=(value)
|
|
521
|
+
@view_props["auto_scroll"] = value
|
|
497
522
|
end
|
|
498
523
|
|
|
499
|
-
def
|
|
500
|
-
|
|
524
|
+
def browser_context_menu
|
|
525
|
+
@view_props["browser_context_menu"]
|
|
526
|
+
end
|
|
501
527
|
|
|
502
|
-
|
|
503
|
-
|
|
528
|
+
def browser_context_menu=(value)
|
|
529
|
+
@view_props["browser_context_menu"] = value
|
|
504
530
|
end
|
|
505
531
|
|
|
506
|
-
def
|
|
507
|
-
|
|
508
|
-
self
|
|
532
|
+
def decoration
|
|
533
|
+
@view_props["decoration"]
|
|
509
534
|
end
|
|
510
535
|
|
|
511
|
-
def
|
|
512
|
-
|
|
536
|
+
def decoration=(value)
|
|
537
|
+
@view_props["decoration"] = value
|
|
538
|
+
end
|
|
513
539
|
|
|
514
|
-
|
|
515
|
-
|
|
540
|
+
def floating_action_button_location
|
|
541
|
+
@view_props["floating_action_button_location"]
|
|
516
542
|
end
|
|
517
543
|
|
|
518
|
-
def
|
|
519
|
-
|
|
520
|
-
|
|
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
|
|
521
586
|
end
|
|
522
587
|
|
|
523
588
|
def dialog = @dialog
|
|
@@ -525,13 +590,15 @@ module Ruflet
|
|
|
525
590
|
def dialog=(value)
|
|
526
591
|
@dialog = value
|
|
527
592
|
refresh_dialogs_container!
|
|
528
|
-
push_dialogs_update! if @dialogs_container_mounted
|
|
529
593
|
end
|
|
530
594
|
|
|
531
595
|
def snack_bar=(value)
|
|
532
596
|
@snack_bar = value
|
|
533
597
|
refresh_dialogs_container!
|
|
534
|
-
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
def snack_bar
|
|
601
|
+
@snack_bar
|
|
535
602
|
end
|
|
536
603
|
|
|
537
604
|
def snackbar=(value)
|
|
@@ -541,12 +608,10 @@ module Ruflet
|
|
|
541
608
|
def bottom_sheet=(value)
|
|
542
609
|
@bottom_sheet = value
|
|
543
610
|
refresh_dialogs_container!
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
# mounted. This is what made NativeApp's modal: sheets fail to open.
|
|
549
|
-
push_dialogs_update! if @dialogs_container_mounted
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
def bottom_sheet
|
|
614
|
+
@bottom_sheet
|
|
550
615
|
end
|
|
551
616
|
|
|
552
617
|
def bottomsheet=(value)
|
|
@@ -559,7 +624,6 @@ module Ruflet
|
|
|
559
624
|
return self if dialog_open?(dialog_control)
|
|
560
625
|
|
|
561
626
|
dialog_control.props["open"] = true
|
|
562
|
-
remove_existing_singleton_dialogs(dialog_control)
|
|
563
627
|
@dialogs << dialog_control unless @dialogs.include?(dialog_control)
|
|
564
628
|
refresh_dialogs_container!
|
|
565
629
|
send_view_patch unless @dialogs_container.wire_id
|
|
@@ -567,6 +631,72 @@ module Ruflet
|
|
|
567
631
|
self
|
|
568
632
|
end
|
|
569
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
|
+
|
|
570
700
|
def invoke(control_or_id, method_name, args: nil, timeout: 10, on_result: nil)
|
|
571
701
|
control_id =
|
|
572
702
|
if page_control_target?(control_or_id)
|
|
@@ -580,7 +710,7 @@ module Ruflet
|
|
|
580
710
|
call_id = "call_#{Ruflet::Control.generate_id}"
|
|
581
711
|
if on_result.respond_to?(:call)
|
|
582
712
|
@invoke_waiters_mutex.synchronize { @invoke_callbacks[call_id] = on_result }
|
|
583
|
-
|
|
713
|
+
unless timeout.nil?
|
|
584
714
|
Thread.new(call_id, timeout.to_f) do |pending_call_id, invoke_timeout|
|
|
585
715
|
sleep([invoke_timeout, 0.0].max + 0.1)
|
|
586
716
|
callback = @invoke_waiters_mutex.synchronize { @invoke_callbacks.delete(pending_call_id) }
|
|
@@ -729,167 +859,6 @@ module Ruflet
|
|
|
729
859
|
upload(files, timeout: timeout, on_result: on_result)
|
|
730
860
|
end
|
|
731
861
|
|
|
732
|
-
def disable_browser_context_menu(timeout: 10, on_result: nil)
|
|
733
|
-
invoke_browser_context_menu("disable_menu", timeout: timeout, on_result: on_result)
|
|
734
|
-
end
|
|
735
|
-
|
|
736
|
-
def enable_browser_context_menu(timeout: 10, on_result: nil)
|
|
737
|
-
invoke_browser_context_menu("enable_menu", timeout: timeout, on_result: on_result)
|
|
738
|
-
end
|
|
739
|
-
|
|
740
|
-
def wait_until_ready_to_show(timeout: 10, on_result: nil)
|
|
741
|
-
invoke_window("wait_until_ready_to_show", timeout: timeout, on_result: on_result)
|
|
742
|
-
end
|
|
743
|
-
|
|
744
|
-
def window_to_front(timeout: 10, on_result: nil)
|
|
745
|
-
invoke_window("to_front", timeout: timeout, on_result: on_result)
|
|
746
|
-
end
|
|
747
|
-
|
|
748
|
-
def center_window(timeout: 10, on_result: nil)
|
|
749
|
-
invoke_window("center", timeout: timeout, on_result: on_result)
|
|
750
|
-
end
|
|
751
|
-
|
|
752
|
-
def close_window(timeout: 10, on_result: nil)
|
|
753
|
-
invoke_window("close", timeout: timeout, on_result: on_result)
|
|
754
|
-
end
|
|
755
|
-
|
|
756
|
-
def destroy_window(timeout: 10, on_result: nil)
|
|
757
|
-
invoke_window("destroy", timeout: timeout, on_result: on_result)
|
|
758
|
-
end
|
|
759
|
-
|
|
760
|
-
def start_window_dragging(timeout: 10, on_result: nil)
|
|
761
|
-
invoke_window("start_dragging", timeout: timeout, on_result: on_result)
|
|
762
|
-
end
|
|
763
|
-
|
|
764
|
-
def start_window_resizing(edge, timeout: 10, on_result: nil)
|
|
765
|
-
invoke_window(
|
|
766
|
-
"start_resizing",
|
|
767
|
-
args: { "edge" => normalize_service_value(edge) },
|
|
768
|
-
timeout: timeout,
|
|
769
|
-
on_result: on_result
|
|
770
|
-
)
|
|
771
|
-
end
|
|
772
|
-
|
|
773
|
-
def tester_pump(options = nil, duration: nil, timeout: 10, on_result: nil)
|
|
774
|
-
duration = options[:duration] || options["duration"] if options.is_a?(Hash) && duration.nil?
|
|
775
|
-
invoke_tester("pump", args: compact_service_args("duration" => duration), timeout: timeout, on_result: on_result)
|
|
776
|
-
end
|
|
777
|
-
|
|
778
|
-
def tester_pump_and_settle(options = nil, duration: nil, timeout: 10, on_result: nil)
|
|
779
|
-
duration = options[:duration] || options["duration"] if options.is_a?(Hash) && duration.nil?
|
|
780
|
-
invoke_tester("pump_and_settle", args: compact_service_args("duration" => duration), timeout: timeout, on_result: on_result)
|
|
781
|
-
end
|
|
782
|
-
|
|
783
|
-
def find_by_text(text, timeout: 10, on_result: nil)
|
|
784
|
-
invoke_tester("find_by_text", args: { "text" => text }, timeout: timeout, on_result: on_result)
|
|
785
|
-
end
|
|
786
|
-
|
|
787
|
-
def find_by_text_containing(pattern, timeout: 10, on_result: nil)
|
|
788
|
-
invoke_tester("find_by_text_containing", args: { "pattern" => pattern }, timeout: timeout, on_result: on_result)
|
|
789
|
-
end
|
|
790
|
-
|
|
791
|
-
def find_by_key(key, timeout: 10, on_result: nil)
|
|
792
|
-
invoke_tester("find_by_key", args: { "key" => key }, timeout: timeout, on_result: on_result)
|
|
793
|
-
end
|
|
794
|
-
|
|
795
|
-
def find_by_tooltip(value, timeout: 10, on_result: nil)
|
|
796
|
-
invoke_tester("find_by_tooltip", args: { "value" => value }, timeout: timeout, on_result: on_result)
|
|
797
|
-
end
|
|
798
|
-
|
|
799
|
-
def find_by_icon(icon, timeout: 10, on_result: nil)
|
|
800
|
-
invoke_tester("find_by_icon", args: { "icon" => normalize_service_value(icon) }, timeout: timeout, on_result: on_result)
|
|
801
|
-
end
|
|
802
|
-
|
|
803
|
-
def take_screenshot(name, timeout: 10, on_result: nil)
|
|
804
|
-
invoke_tester("take_screenshot", args: { "name" => name }, timeout: timeout, on_result: on_result)
|
|
805
|
-
end
|
|
806
|
-
|
|
807
|
-
def tap(finder_id = nil, options = nil, finder_index: nil, timeout: 10, on_result: nil)
|
|
808
|
-
finder_index = options[:finder_index] || options["finder_index"] if options.is_a?(Hash) && finder_index.nil?
|
|
809
|
-
invoke_tester_finder("tap", finder_id, finder_index: finder_index, timeout: timeout, on_result: on_result)
|
|
810
|
-
end
|
|
811
|
-
|
|
812
|
-
def mouse_click(finder_id = nil, options = nil, finder_index: nil, timeout: 10, on_result: nil)
|
|
813
|
-
finder_index = options[:finder_index] || options["finder_index"] if options.is_a?(Hash) && finder_index.nil?
|
|
814
|
-
invoke_tester_finder("mouse_click", finder_id, finder_index: finder_index, timeout: timeout, on_result: on_result)
|
|
815
|
-
end
|
|
816
|
-
|
|
817
|
-
def mouse_double_click(finder_id = nil, options = nil, finder_index: nil, timeout: 10, on_result: nil)
|
|
818
|
-
finder_index = options[:finder_index] || options["finder_index"] if options.is_a?(Hash) && finder_index.nil?
|
|
819
|
-
invoke_tester_finder("mouse_double_click", finder_id, finder_index: finder_index, timeout: timeout, on_result: on_result)
|
|
820
|
-
end
|
|
821
|
-
|
|
822
|
-
def right_mouse_click(finder_id = nil, options = nil, finder_index: nil, timeout: 10, on_result: nil)
|
|
823
|
-
finder_index = options[:finder_index] || options["finder_index"] if options.is_a?(Hash) && finder_index.nil?
|
|
824
|
-
invoke_tester_finder("right_mouse_click", finder_id, finder_index: finder_index, timeout: timeout, on_result: on_result)
|
|
825
|
-
end
|
|
826
|
-
|
|
827
|
-
def tap_at(offset = nil, timeout: 10, on_result: nil)
|
|
828
|
-
invoke_tester_at("tap_at", offset, timeout: timeout, on_result: on_result)
|
|
829
|
-
end
|
|
830
|
-
|
|
831
|
-
def mouse_click_at(offset = nil, timeout: 10, on_result: nil)
|
|
832
|
-
invoke_tester_at("mouse_click_at", offset, timeout: timeout, on_result: on_result)
|
|
833
|
-
end
|
|
834
|
-
|
|
835
|
-
def mouse_double_click_at(offset = nil, timeout: 10, on_result: nil)
|
|
836
|
-
invoke_tester_at("mouse_double_click_at", offset, timeout: timeout, on_result: on_result)
|
|
837
|
-
end
|
|
838
|
-
|
|
839
|
-
def right_mouse_click_at(offset = nil, timeout: 10, on_result: nil)
|
|
840
|
-
invoke_tester_at("right_mouse_click_at", offset, timeout: timeout, on_result: on_result)
|
|
841
|
-
end
|
|
842
|
-
|
|
843
|
-
def drag(finder_id, offset, finder_index: nil, timeout: 10, on_result: nil)
|
|
844
|
-
invoke_tester(
|
|
845
|
-
"drag",
|
|
846
|
-
args: compact_service_args(
|
|
847
|
-
"finder_id" => finder_id,
|
|
848
|
-
"finder_index" => finder_index,
|
|
849
|
-
"offset" => offset
|
|
850
|
-
),
|
|
851
|
-
timeout: timeout,
|
|
852
|
-
on_result: on_result
|
|
853
|
-
)
|
|
854
|
-
end
|
|
855
|
-
|
|
856
|
-
def drag_from(start, offset, timeout: 10, on_result: nil)
|
|
857
|
-
invoke_tester(
|
|
858
|
-
"drag_from",
|
|
859
|
-
args: compact_service_args("start" => start, "offset" => offset),
|
|
860
|
-
timeout: timeout,
|
|
861
|
-
on_result: on_result
|
|
862
|
-
)
|
|
863
|
-
end
|
|
864
|
-
|
|
865
|
-
def long_press(finder_id = nil, options = nil, finder_index: nil, timeout: 10, on_result: nil)
|
|
866
|
-
finder_index = options[:finder_index] || options["finder_index"] if options.is_a?(Hash) && finder_index.nil?
|
|
867
|
-
invoke_tester_finder("long_press", finder_id, finder_index: finder_index, timeout: timeout, on_result: on_result)
|
|
868
|
-
end
|
|
869
|
-
|
|
870
|
-
def enter_text(finder_id, text, options = nil, finder_index: nil, timeout: 10, on_result: nil)
|
|
871
|
-
finder_index = options[:finder_index] || options["finder_index"] if options.is_a?(Hash) && finder_index.nil?
|
|
872
|
-
invoke_tester(
|
|
873
|
-
"enter_text",
|
|
874
|
-
args: compact_service_args(
|
|
875
|
-
"finder_id" => finder_id,
|
|
876
|
-
"finder_index" => finder_index,
|
|
877
|
-
"text" => text
|
|
878
|
-
),
|
|
879
|
-
timeout: timeout,
|
|
880
|
-
on_result: on_result
|
|
881
|
-
)
|
|
882
|
-
end
|
|
883
|
-
|
|
884
|
-
def mouse_hover(finder_id = nil, options = nil, finder_index: nil, timeout: 10, on_result: nil)
|
|
885
|
-
finder_index = options[:finder_index] || options["finder_index"] if options.is_a?(Hash) && finder_index.nil?
|
|
886
|
-
invoke_tester_finder("mouse_hover", finder_id, finder_index: finder_index, timeout: timeout, on_result: on_result)
|
|
887
|
-
end
|
|
888
|
-
|
|
889
|
-
def tester_teardown(timeout: 10, on_result: nil)
|
|
890
|
-
invoke_tester("teardown", timeout: timeout, on_result: on_result)
|
|
891
|
-
end
|
|
892
|
-
|
|
893
862
|
def heavy_impact(timeout: 10, on_result: nil)
|
|
894
863
|
invoke_haptic_feedback("heavy_impact", timeout: timeout, on_result: on_result)
|
|
895
864
|
end
|
|
@@ -989,6 +958,18 @@ module Ruflet
|
|
|
989
958
|
service(:barometer, **props)
|
|
990
959
|
end
|
|
991
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
|
+
|
|
992
973
|
def shake_detector(**props)
|
|
993
974
|
service(:shake_detector, **props)
|
|
994
975
|
end
|
|
@@ -998,7 +979,7 @@ module Ruflet
|
|
|
998
979
|
end
|
|
999
980
|
|
|
1000
981
|
def screenshot(**props)
|
|
1001
|
-
|
|
982
|
+
Ruflet::UI::ControlFactory.build(:screenshot, **props)
|
|
1002
983
|
end
|
|
1003
984
|
|
|
1004
985
|
def battery(**props)
|
|
@@ -1037,18 +1018,6 @@ module Ruflet
|
|
|
1037
1018
|
service(:haptic_feedback, **props)
|
|
1038
1019
|
end
|
|
1039
1020
|
|
|
1040
|
-
def geolocator(**props)
|
|
1041
|
-
service(:geolocator, **props)
|
|
1042
|
-
end
|
|
1043
|
-
|
|
1044
|
-
def permission_handler(**props)
|
|
1045
|
-
service(:permission_handler, **props)
|
|
1046
|
-
end
|
|
1047
|
-
|
|
1048
|
-
def secure_storage(**props)
|
|
1049
|
-
service(:secure_storage, **props)
|
|
1050
|
-
end
|
|
1051
|
-
|
|
1052
1021
|
def get_application_cache_directory(timeout: nil, on_result: nil)
|
|
1053
1022
|
invoke_storage_paths("get_application_cache_directory", timeout: timeout, on_result: on_result)
|
|
1054
1023
|
end
|
|
@@ -1193,27 +1162,7 @@ module Ruflet
|
|
|
1193
1162
|
end
|
|
1194
1163
|
|
|
1195
1164
|
def pop_dialog
|
|
1196
|
-
|
|
1197
|
-
return nil unless dialog_control
|
|
1198
|
-
|
|
1199
|
-
dialog_control.props["open"] = false
|
|
1200
|
-
update(dialog_control, open: false)
|
|
1201
|
-
dialog_control
|
|
1202
|
-
end
|
|
1203
|
-
|
|
1204
|
-
def close_dialog(dialog_control)
|
|
1205
|
-
return self unless dialog_control
|
|
1206
|
-
|
|
1207
|
-
dialog_control.props["open"] = false
|
|
1208
|
-
@dialog = nil if @dialog.equal?(dialog_control)
|
|
1209
|
-
remove_dialog_tracking(dialog_control)
|
|
1210
|
-
refresh_dialogs_container!
|
|
1211
|
-
# Patch the dialogs container in place. Forcing a full view re-render
|
|
1212
|
-
# here would remount the whole overlay — fatal while another dialog
|
|
1213
|
-
# (e.g. the form behind a nested picker) is still open. The empty case
|
|
1214
|
-
# is handled inside push_dialogs_update!.
|
|
1215
|
-
push_dialogs_update!
|
|
1216
|
-
self
|
|
1165
|
+
close_dialog
|
|
1217
1166
|
end
|
|
1218
1167
|
|
|
1219
1168
|
def update(control_or_id = nil, **props)
|
|
@@ -1251,7 +1200,6 @@ module Ruflet
|
|
|
1251
1200
|
|
|
1252
1201
|
visited = Set.new
|
|
1253
1202
|
patch.each_value { |value| register_embedded_value(value, visited) }
|
|
1254
|
-
patch.each { |k, v| control.props[k] = v }
|
|
1255
1203
|
|
|
1256
1204
|
patch_ops = patch.map { |k, v| [0, 0, k, serialize_patch_value(v)] }
|
|
1257
1205
|
|
|
@@ -1263,6 +1211,10 @@ module Ruflet
|
|
|
1263
1211
|
self
|
|
1264
1212
|
end
|
|
1265
1213
|
|
|
1214
|
+
def schedule_update
|
|
1215
|
+
update
|
|
1216
|
+
end
|
|
1217
|
+
|
|
1266
1218
|
def patch_page(control_id, **props)
|
|
1267
1219
|
update(control_id, **props)
|
|
1268
1220
|
end
|
|
@@ -1274,6 +1226,8 @@ module Ruflet
|
|
|
1274
1226
|
patch = normalize_props(props || {})
|
|
1275
1227
|
patch.each { |k, v| control.props[k] = v }
|
|
1276
1228
|
|
|
1229
|
+
remove_dialog_tracking(control) if patch.key?("open") && patch["open"] == false
|
|
1230
|
+
|
|
1277
1231
|
self
|
|
1278
1232
|
end
|
|
1279
1233
|
|
|
@@ -1281,17 +1235,8 @@ module Ruflet
|
|
|
1281
1235
|
if page_control_target?(target)
|
|
1282
1236
|
if name.to_s == "route_change"
|
|
1283
1237
|
route_from_event = extract_route(data)
|
|
1284
|
-
|
|
1285
|
-
# Navigating away must dismiss them, or they ghost onto the next
|
|
1286
|
-
# view — the picker that "reappears after going home".
|
|
1287
|
-
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"]
|
|
1288
1239
|
@page_props["route"] = route_from_event if route_from_event
|
|
1289
|
-
elsif name.to_s == "resize"
|
|
1290
|
-
# The client reports the live page size via the "resize" event. Store
|
|
1291
|
-
# it so `page.width`/`page.height` reflect the real viewport — without
|
|
1292
|
-
# this, responsive layouts collapse on clients (e.g. embedded/iOS)
|
|
1293
|
-
# that don't know their size at the initial handshake.
|
|
1294
|
-
store_reported_page_size(data)
|
|
1295
1240
|
end
|
|
1296
1241
|
dispatch_page_event(name: name, data: data)
|
|
1297
1242
|
return
|
|
@@ -1300,19 +1245,13 @@ module Ruflet
|
|
|
1300
1245
|
control = @wire_index[target.to_i] || @control_index[target.to_s]
|
|
1301
1246
|
return unless control
|
|
1302
1247
|
|
|
1303
|
-
event =
|
|
1248
|
+
event = Event.new(name: name, target: target, raw_data: data, page: self, control: control)
|
|
1304
1249
|
apply_event_value_to_control(control, event) if %w[change select select_change].include?(name.to_s)
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
mark_picker_dialog_closed(control, name)
|
|
1309
|
-
if dialog_close_event?(control, name) && remove_dialog_tracking(control)
|
|
1310
|
-
# Patch the container in place; never force a full view re-render that
|
|
1311
|
-
# 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)
|
|
1312
1253
|
push_dialogs_update!
|
|
1313
1254
|
end
|
|
1314
|
-
|
|
1315
|
-
control.emit(name, event)
|
|
1316
1255
|
end
|
|
1317
1256
|
|
|
1318
1257
|
def method_missing(name, *args, &block)
|
|
@@ -1330,6 +1269,9 @@ module Ruflet
|
|
|
1330
1269
|
if args.empty? && !block
|
|
1331
1270
|
return @page_props[method_name] if @page_props.key?(method_name)
|
|
1332
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)
|
|
1333
1275
|
return instance_variable_get("@#{method_name}") if DIALOG_PROP_KEYS.include?(method_name)
|
|
1334
1276
|
end
|
|
1335
1277
|
|
|
@@ -1354,14 +1296,20 @@ module Ruflet
|
|
|
1354
1296
|
|
|
1355
1297
|
private
|
|
1356
1298
|
|
|
1357
|
-
def
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
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
|
|
1365
1313
|
end
|
|
1366
1314
|
|
|
1367
1315
|
def invoke_and_wait(control_or_id, method_name, args: nil, timeout: 10)
|
|
@@ -1447,6 +1395,14 @@ module Ruflet
|
|
|
1447
1395
|
type.to_s.delete("_") == "camera"
|
|
1448
1396
|
end
|
|
1449
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
|
+
|
|
1450
1406
|
def text_maps_to_content?(control, patch)
|
|
1451
1407
|
patch.key?("text") && control.type.end_with?("button")
|
|
1452
1408
|
end
|
|
@@ -1461,12 +1417,14 @@ module Ruflet
|
|
|
1461
1417
|
@sender.call(action, payload)
|
|
1462
1418
|
end
|
|
1463
1419
|
|
|
1464
|
-
def
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
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
|
|
1470
1428
|
end
|
|
1471
1429
|
|
|
1472
1430
|
def send_view_patch
|
|
@@ -1478,8 +1436,8 @@ module Ruflet
|
|
|
1478
1436
|
"id" => 1,
|
|
1479
1437
|
"patch" => [
|
|
1480
1438
|
[0],
|
|
1481
|
-
|
|
1482
|
-
|
|
1439
|
+
[0, 0, "views", view_patches],
|
|
1440
|
+
*page_patch_ops
|
|
1483
1441
|
]
|
|
1484
1442
|
})
|
|
1485
1443
|
@overlay_container_mounted = true if @overlay_container.wire_id
|
|
@@ -1574,7 +1532,6 @@ module Ruflet
|
|
|
1574
1532
|
raise ArgumentError, "page #{key} must use an icon name string, not #{value.inspect}"
|
|
1575
1533
|
end
|
|
1576
1534
|
|
|
1577
|
-
value = Ruflet::Colors.normalize_property(key, value)
|
|
1578
1535
|
return value.value if value.is_a?(Ruflet::IconData)
|
|
1579
1536
|
value.is_a?(Symbol) ? value.to_s : value
|
|
1580
1537
|
end
|
|
@@ -1600,14 +1557,9 @@ module Ruflet
|
|
|
1600
1557
|
query_string = route_value.to_s.split("?", 2)[1].to_s
|
|
1601
1558
|
return {} if query_string.empty?
|
|
1602
1559
|
|
|
1603
|
-
query_string.
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
key, value = pair.split("=", 2)
|
|
1607
|
-
key = CGI.unescape(key.to_s.tr("+", " "))
|
|
1608
|
-
value = CGI.unescape(value.to_s.tr("+", " "))
|
|
1609
|
-
previous = result[key]
|
|
1610
|
-
result[key] = previous.nil? ? value : Array(previous) << value
|
|
1560
|
+
URI.decode_www_form(query_string).group_by(&:first).each_with_object({}) do |(key, pairs), result|
|
|
1561
|
+
values = pairs.map(&:last)
|
|
1562
|
+
result[key] = values.size == 1 ? values.first : values
|
|
1611
1563
|
end
|
|
1612
1564
|
end
|
|
1613
1565
|
|
|
@@ -1622,20 +1574,13 @@ module Ruflet
|
|
|
1622
1574
|
end
|
|
1623
1575
|
end
|
|
1624
1576
|
|
|
1625
|
-
def store_reported_page_size(data)
|
|
1626
|
-
return unless data.is_a?(Hash)
|
|
1627
|
-
|
|
1628
|
-
width = data["width"] || data[:width]
|
|
1629
|
-
height = data["height"] || data[:height]
|
|
1630
|
-
@page_props["width"] = width unless width.nil?
|
|
1631
|
-
@page_props["height"] = height unless height.nil?
|
|
1632
|
-
end
|
|
1633
|
-
|
|
1634
1577
|
def dispatch_page_event(name:, data:)
|
|
1635
|
-
|
|
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]
|
|
1636
1581
|
return unless handler.respond_to?(:call)
|
|
1637
1582
|
|
|
1638
|
-
event =
|
|
1583
|
+
event = Event.new(name: name.to_s, target: 1, raw_data: data, page: self, control: nil)
|
|
1639
1584
|
handler.call(event)
|
|
1640
1585
|
end
|
|
1641
1586
|
|
|
@@ -1651,8 +1596,8 @@ module Ruflet
|
|
|
1651
1596
|
end_value = range_value["end_value"] || range_value[:end_value]
|
|
1652
1597
|
control.props["start_value"] = start_value unless start_value.nil?
|
|
1653
1598
|
control.props["end_value"] = end_value unless end_value.nil?
|
|
1599
|
+
return
|
|
1654
1600
|
end
|
|
1655
|
-
return
|
|
1656
1601
|
end
|
|
1657
1602
|
|
|
1658
1603
|
return if value.nil?
|
|
@@ -1699,7 +1644,7 @@ module Ruflet
|
|
|
1699
1644
|
end
|
|
1700
1645
|
|
|
1701
1646
|
def refresh_dialogs_container!
|
|
1702
|
-
dialog_controls = (
|
|
1647
|
+
dialog_controls = (@dialogs + dialog_slots).uniq
|
|
1703
1648
|
@dialogs_container.props["controls"] = dialog_controls
|
|
1704
1649
|
@page_props["_dialogs"] = @dialogs_container
|
|
1705
1650
|
end
|
|
@@ -1708,6 +1653,19 @@ module Ruflet
|
|
|
1708
1653
|
@page_props["_overlay"] = @overlay_container
|
|
1709
1654
|
end
|
|
1710
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
|
+
|
|
1711
1669
|
def refresh_services_container!
|
|
1712
1670
|
@page_props["_services"] = @services_container
|
|
1713
1671
|
end
|
|
@@ -1715,72 +1673,19 @@ module Ruflet
|
|
|
1715
1673
|
def push_services_update!
|
|
1716
1674
|
refresh_control_indexes!
|
|
1717
1675
|
|
|
1718
|
-
|
|
1719
|
-
# First mount: the whole list ships inside the page/view patch.
|
|
1720
|
-
send_view_patch
|
|
1721
|
-
@mounted_services = Array(@services_container.props["_services"]).dup
|
|
1722
|
-
return
|
|
1723
|
-
end
|
|
1724
|
-
|
|
1725
|
-
current = Array(@services_container.props["_services"])
|
|
1726
|
-
ops = services_patch_ops(@mounted_services, current)
|
|
1727
|
-
unless ops.empty?
|
|
1728
|
-
# Incrementally add/remove individual services. Re-sending the whole
|
|
1729
|
-
# `_services` list as full objects (a single replace op) makes the
|
|
1730
|
-
# client run Control.fromMap for EVERY service, replacing each Control
|
|
1731
|
-
# instance while ServiceRegistry only binds ids it hasn't seen — so an
|
|
1732
|
-
# already-mounted service (share, clipboard, haptic) keeps its old
|
|
1733
|
-
# binding while controlsIndex points at the fresh, listener-less
|
|
1734
|
-
# instance, and its next invoke hangs. That was the "after Copy, Share
|
|
1735
|
-
# stops working" bug. Add/remove ops leave untouched services intact.
|
|
1676
|
+
if @services_container.wire_id
|
|
1736
1677
|
send_message(Protocol::ACTIONS[:patch_control], {
|
|
1737
1678
|
"id" => @services_container.wire_id,
|
|
1738
|
-
"patch" => [[0,
|
|
1679
|
+
"patch" => [[0], [0, 0, "_services", serialize_patch_value(@services_container.props["_services"])]]
|
|
1739
1680
|
})
|
|
1681
|
+
else
|
|
1682
|
+
send_view_patch
|
|
1740
1683
|
end
|
|
1741
|
-
@mounted_services = current.dup
|
|
1742
|
-
end
|
|
1743
|
-
|
|
1744
|
-
# Diff the previously-mounted services list against the current one and emit
|
|
1745
|
-
# add (1) / remove (2) ops against the `_services` list (patch node 1).
|
|
1746
|
-
# Services are normally append-only; a moved entry is expressed as
|
|
1747
|
-
# remove + add. Existing entries are never re-serialized, so their client
|
|
1748
|
-
# Control instances (and invoke listeners) survive.
|
|
1749
|
-
def services_patch_ops(previous, current)
|
|
1750
|
-
ops = []
|
|
1751
|
-
working = previous.dup
|
|
1752
|
-
|
|
1753
|
-
(working.length - 1).downto(0) do |index|
|
|
1754
|
-
next if current.any? { |service| service.equal?(working[index]) }
|
|
1755
|
-
|
|
1756
|
-
ops << [2, 1, index]
|
|
1757
|
-
working.delete_at(index)
|
|
1758
|
-
end
|
|
1759
|
-
|
|
1760
|
-
current.each_with_index do |service, index|
|
|
1761
|
-
next if working[index]&.equal?(service)
|
|
1762
|
-
|
|
1763
|
-
existing = working.index { |candidate| candidate.equal?(service) }
|
|
1764
|
-
if existing
|
|
1765
|
-
ops << [2, 1, existing]
|
|
1766
|
-
working.delete_at(existing)
|
|
1767
|
-
end
|
|
1768
|
-
ops << [1, 1, index, serialize_patch_value(service)]
|
|
1769
|
-
working.insert(index, service)
|
|
1770
|
-
end
|
|
1771
|
-
|
|
1772
|
-
ops
|
|
1773
1684
|
end
|
|
1774
1685
|
|
|
1775
1686
|
def push_dialogs_update!
|
|
1776
1687
|
refresh_control_indexes!
|
|
1777
1688
|
|
|
1778
|
-
# Once the dialogs container is mounted, every change — opening, closing,
|
|
1779
|
-
# even down to no dialogs at all — is an in-place patch of its controls
|
|
1780
|
-
# list. Re-sending the view (or the container as a whole object) would
|
|
1781
|
-
# replace the live container instance on the Flutter side, detaching its
|
|
1782
|
-
# listeners and breaking any other dialog still open. Only the very first
|
|
1783
|
-
# dialog, before the container has a wire id, needs a view patch to mount.
|
|
1784
1689
|
if @dialogs_container.wire_id
|
|
1785
1690
|
send_message(Protocol::ACTIONS[:patch_control], {
|
|
1786
1691
|
"id" => @dialogs_container.wire_id,
|
|
@@ -1803,41 +1708,6 @@ module Ruflet
|
|
|
1803
1708
|
@dialogs.include?(dialog_control) && dialog_control.props["open"] == true
|
|
1804
1709
|
end
|
|
1805
1710
|
|
|
1806
|
-
def dialog_close_event?(control, name)
|
|
1807
|
-
name = name.to_s
|
|
1808
|
-
name == "dismiss" || (%w[change select select_change].include?(name) && @dialogs.include?(control) && control.props["open"] == false)
|
|
1809
|
-
end
|
|
1810
|
-
|
|
1811
|
-
# Picker dialogs that auto-dismiss on the client after a selection. Their
|
|
1812
|
-
# confirm sends a value event (change/select), not a close, so the server
|
|
1813
|
-
# must flip `open` to false or show_dialog's open-guard blocks reopening.
|
|
1814
|
-
PICKER_DIALOG_TYPES = %w[
|
|
1815
|
-
datepicker daterangepicker timepicker
|
|
1816
|
-
cupertinodatepicker cupertinotimerpicker
|
|
1817
|
-
].freeze
|
|
1818
|
-
|
|
1819
|
-
def picker_dialog?(control)
|
|
1820
|
-
PICKER_DIALOG_TYPES.include?(control.type.to_s.tr("_", "").downcase)
|
|
1821
|
-
end
|
|
1822
|
-
|
|
1823
|
-
def mark_picker_dialog_closed(control, name)
|
|
1824
|
-
return unless picker_dialog?(control)
|
|
1825
|
-
return unless %w[change select select_change dismiss].include?(name.to_s)
|
|
1826
|
-
|
|
1827
|
-
control.props["open"] = false
|
|
1828
|
-
end
|
|
1829
|
-
|
|
1830
|
-
# Close and untrack every dialog currently shown. Called on navigation so
|
|
1831
|
-
# a dialog opened in one view does not linger as an overlay on the next.
|
|
1832
|
-
def dismiss_tracked_dialogs!
|
|
1833
|
-
return if @dialogs.empty?
|
|
1834
|
-
|
|
1835
|
-
@dialogs.each { |dialog| dialog.props["open"] = false }
|
|
1836
|
-
@dialogs.clear
|
|
1837
|
-
refresh_dialogs_container!
|
|
1838
|
-
push_dialogs_update! if @dialogs_container_mounted
|
|
1839
|
-
end
|
|
1840
|
-
|
|
1841
1711
|
def remove_dialog_tracking(control)
|
|
1842
1712
|
return false unless @dialogs.include?(control)
|
|
1843
1713
|
|
|
@@ -1846,16 +1716,6 @@ module Ruflet
|
|
|
1846
1716
|
true
|
|
1847
1717
|
end
|
|
1848
1718
|
|
|
1849
|
-
def remove_existing_singleton_dialogs(control)
|
|
1850
|
-
return unless singleton_dialog_control?(control)
|
|
1851
|
-
|
|
1852
|
-
@dialogs.delete_if { |dialog| dialog != control && singleton_dialog_control?(dialog) }
|
|
1853
|
-
end
|
|
1854
|
-
|
|
1855
|
-
def singleton_dialog_control?(control)
|
|
1856
|
-
control.type.to_s.tr("_", "").downcase == "snackbar"
|
|
1857
|
-
end
|
|
1858
|
-
|
|
1859
1719
|
def assign_split_prop(key, value)
|
|
1860
1720
|
if key == "vertical_alignment" || key == "horizontal_alignment"
|
|
1861
1721
|
@page_props[key] = value
|
|
@@ -1902,7 +1762,6 @@ module Ruflet
|
|
|
1902
1762
|
existing = service_by_type(type)
|
|
1903
1763
|
return [existing, false] if existing
|
|
1904
1764
|
|
|
1905
|
-
# `service` already syncs via add_service -> push_services_update!.
|
|
1906
1765
|
[service(type), true]
|
|
1907
1766
|
end
|
|
1908
1767
|
|
|
@@ -1927,51 +1786,6 @@ module Ruflet
|
|
|
1927
1786
|
service(:url_launcher)
|
|
1928
1787
|
end
|
|
1929
1788
|
|
|
1930
|
-
def ensure_browser_context_menu_service
|
|
1931
|
-
service(:browser_context_menu)
|
|
1932
|
-
end
|
|
1933
|
-
|
|
1934
|
-
def invoke_browser_context_menu(method_name, timeout:, on_result:)
|
|
1935
|
-
browser_context_menu = ensure_browser_context_menu_service
|
|
1936
|
-
invoke(browser_context_menu, method_name, timeout: timeout, on_result: on_result)
|
|
1937
|
-
end
|
|
1938
|
-
|
|
1939
|
-
def ensure_window_service
|
|
1940
|
-
service(:window)
|
|
1941
|
-
end
|
|
1942
|
-
|
|
1943
|
-
def invoke_window(method_name, args: nil, timeout:, on_result:)
|
|
1944
|
-
window = ensure_window_service
|
|
1945
|
-
invoke(window, method_name, args: args, timeout: timeout, on_result: on_result)
|
|
1946
|
-
end
|
|
1947
|
-
|
|
1948
|
-
def ensure_tester_service
|
|
1949
|
-
service(:tester)
|
|
1950
|
-
end
|
|
1951
|
-
|
|
1952
|
-
def invoke_tester(method_name, args: nil, timeout:, on_result:)
|
|
1953
|
-
tester = ensure_tester_service
|
|
1954
|
-
invoke(tester, method_name, args: args, timeout: timeout, on_result: on_result)
|
|
1955
|
-
end
|
|
1956
|
-
|
|
1957
|
-
def invoke_tester_finder(method_name, finder_id, finder_index:, timeout:, on_result:)
|
|
1958
|
-
invoke_tester(
|
|
1959
|
-
method_name,
|
|
1960
|
-
args: compact_service_args("finder_id" => finder_id, "finder_index" => finder_index),
|
|
1961
|
-
timeout: timeout,
|
|
1962
|
-
on_result: on_result
|
|
1963
|
-
)
|
|
1964
|
-
end
|
|
1965
|
-
|
|
1966
|
-
def invoke_tester_at(method_name, offset, timeout:, on_result:)
|
|
1967
|
-
invoke_tester(
|
|
1968
|
-
method_name,
|
|
1969
|
-
args: compact_service_args("offset" => offset),
|
|
1970
|
-
timeout: timeout,
|
|
1971
|
-
on_result: on_result
|
|
1972
|
-
)
|
|
1973
|
-
end
|
|
1974
|
-
|
|
1975
1789
|
def ensure_haptic_feedback_service
|
|
1976
1790
|
service(:haptic_feedback)
|
|
1977
1791
|
end
|
|
@@ -1981,38 +1795,6 @@ module Ruflet
|
|
|
1981
1795
|
invoke(haptic_feedback, method_name, timeout: timeout, on_result: on_result)
|
|
1982
1796
|
end
|
|
1983
1797
|
|
|
1984
|
-
def invoke_current_view(method_name, timeout:, on_result:)
|
|
1985
|
-
target_id = @views.last&.wire_id || @view_id
|
|
1986
|
-
invoke_control_id(target_id, method_name, timeout: timeout, on_result: on_result)
|
|
1987
|
-
end
|
|
1988
|
-
|
|
1989
|
-
def invoke_control_id(control_id, method_name, args: nil, timeout: 10, on_result: nil)
|
|
1990
|
-
call_id = "call_#{Ruflet::Control.generate_id}"
|
|
1991
|
-
if on_result.respond_to?(:call)
|
|
1992
|
-
@invoke_waiters_mutex.synchronize { @invoke_callbacks[call_id] = on_result }
|
|
1993
|
-
if embedded_async_timeout_available? && !timeout.nil?
|
|
1994
|
-
Thread.new(call_id, timeout.to_f) do |pending_call_id, invoke_timeout|
|
|
1995
|
-
sleep([invoke_timeout, 0.0].max + 0.1)
|
|
1996
|
-
callback = @invoke_waiters_mutex.synchronize { @invoke_callbacks.delete(pending_call_id) }
|
|
1997
|
-
callback&.call(nil, "execution expired")
|
|
1998
|
-
rescue StandardError => e
|
|
1999
|
-
Kernel.warn("invoke timeout callback error: #{e.class}: #{e.message}")
|
|
2000
|
-
end
|
|
2001
|
-
end
|
|
2002
|
-
end
|
|
2003
|
-
|
|
2004
|
-
payload = {
|
|
2005
|
-
"control_id" => control_id,
|
|
2006
|
-
"call_id" => call_id,
|
|
2007
|
-
"name" => method_name.to_s,
|
|
2008
|
-
"args" => args
|
|
2009
|
-
}
|
|
2010
|
-
payload["timeout"] = timeout unless timeout.nil?
|
|
2011
|
-
send_message(Protocol::ACTIONS[:invoke_control_method], payload)
|
|
2012
|
-
|
|
2013
|
-
call_id
|
|
2014
|
-
end
|
|
2015
|
-
|
|
2016
1798
|
def ensure_connectivity_service
|
|
2017
1799
|
service_with_created(:connectivity)
|
|
2018
1800
|
end
|