Class: Ruflet::Page

Inherits:
Object
  • Object
show all
Defined in:
lib/ruflet_ui/ruflet/page.rb

Defined Under Namespace

Classes: FlashlightService, ScreenBrightnessService, SharedPreferencesService, WakelockService

Constant Summary collapse

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
DIALOG_PROP_KEYS =
%w[dialog snack_bar bottom_sheet].freeze
PAGE_ADD_RESERVED_KEYS =
%i[appbar bottom_appbar floating_action_button navigation_bar dialog snack_bar bottom_sheet].freeze
WIDGET_HELPER_METHODS =
(
  Ruflet::UI::MaterialControlMethods.instance_methods(false) +
  Ruflet::UI::CupertinoControlMethods.instance_methods(false) +
  %i[control widget]
).map(&:to_s).uniq.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(session_id:, client_details:, sender:) ⇒ Page

Returns a new instance of Page.



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/ruflet_ui/ruflet/page.rb', line 163

def initialize(session_id:, client_details:, sender:)
  @session_id = session_id
  @client_details = client_details
  @sender = sender
  @control_index = {}
  @wire_index = {}
  @next_wire_id = 100
  @view_id = 20
  @root_controls = []
  @views = []
  @dialogs = []
  @overlay_container_mounted = false
  @dialogs_container_mounted = false
  @services_container_mounted = false
  @visual_service_controls = {}
  @page_event_handlers = {}
  @view_props = {}
  @page_props = { "route" => (client_details["route"] || "/") }
  @window = build_client_window(client_details["window"])
  @page_props["window"] = @window
  @overlay_container = Ruflet::Control.new(
    type: "overlay",
    id: "_overlay",
    controls: []
  )
  @services_container = Ruflet::Control.new(
    type: "service_registry",
    id: "_services",
    "_services": [],
    "_internals": { "uid" => Ruflet::Control.generate_id }
  )
  @dialogs_container = Ruflet::Control.new(
    type: "dialogs",
    id: "_dialogs",
    controls: []
  )
  @invoke_waiters = {}
  @invoke_callbacks = {}
  @invoke_waiters_mutex = Mutex.new
  @shared_preferences_proxy = SharedPreferencesService.new(self)
  @wakelock_proxy = WakelockService.new(self)
  @flashlight_proxy = FlashlightService.new(self)
  @screen_brightness_proxy = ScreenBrightnessService.new(self)
  refresh_overlay_container!
  refresh_services_container!
  refresh_dialogs_container!
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Object



1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
# File 'lib/ruflet_ui/ruflet/page.rb', line 1265

def method_missing(name, *args, &block)
  method_name = name.to_s
  prop_name = method_name.delete_suffix("=")

  if method_name.end_with?("=")
    if widget_helper_method?(prop_name)
      raise NoMethodError, "Use `#{prop_name}(...)` as a free widget helper, then attach with `page.add(...)`."
    end
    assign_split_prop(prop_name, normalize_value(prop_name, args.first))
    return args.first
  end

  if args.empty? && !block
    return @page_props[method_name] if @page_props.key?(method_name)
    return @view_props[method_name] if @view_props.key?(method_name)
    # Client-reported page properties (width, height, platform,
    # platform_brightness, media) arrive in the register payload.
    return @client_details[method_name] if @client_details.respond_to?(:key?) && @client_details.key?(method_name)
    return instance_variable_get("@#{method_name}") if DIALOG_PROP_KEYS.include?(method_name)
  end

  if widget_helper_method?(name)
    raise NoMethodError, "Use `#{name}(...)` as a free widget helper, then attach with `page.add(...)`."
  end

  super
end

Instance Attribute Details

#client_detailsObject (readonly)

Returns the value of attribute client_details.



161
162
163
# File 'lib/ruflet_ui/ruflet/page.rb', line 161

def client_details
  @client_details
end

#session_idObject (readonly)

Returns the value of attribute session_id.



161
162
163
# File 'lib/ruflet_ui/ruflet/page.rb', line 161

def session_id
  @session_id
end

#viewsObject

Returns the value of attribute views.



161
162
163
# File 'lib/ruflet_ui/ruflet/page.rb', line 161

def views
  @views
end

#windowObject (readonly)

Returns the value of attribute window.



161
162
163
# File 'lib/ruflet_ui/ruflet/page.rb', line 161

def window
  @window
end

Instance Method Details

#accelerometer(**props) ⇒ Object



947
948
949
# File 'lib/ruflet_ui/ruflet/page.rb', line 947

def accelerometer(**props)
  service(:accelerometer, **props)
end

#add(control) ⇒ Object

Raises:

  • (ArgumentError)


260
261
262
263
264
265
266
267
268
269
# File 'lib/ruflet_ui/ruflet/page.rb', line 260

def add(control)
  if control.is_a?(Hash) && (control.keys.map(&:to_sym) & PAGE_ADD_RESERVED_KEYS).any?
    raise ArgumentError, "Page#add accepts only controls; assign page.appbar, page.floating_action_button, dialogs, or other page properties before calling add"
  end

  raise ArgumentError, "Page#add accepts exactly one control" unless control.is_a?(Control)

  replace_root_controls(@root_controls + [control])
  self
end

#add_service(*value) ⇒ Object



366
367
368
369
370
371
# File 'lib/ruflet_ui/ruflet/page.rb', line 366

def add_service(*value)
  @services_container.props["_services"] = services + value.flatten.compact
  refresh_services_container!
  push_services_update!
  self
end

#appbarObject



480
481
482
# File 'lib/ruflet_ui/ruflet/page.rb', line 480

def appbar
  @view_props["appbar"]
end

#appbar=(value) ⇒ Object



484
485
486
# File 'lib/ruflet_ui/ruflet/page.rb', line 484

def appbar=(value)
  @view_props["appbar"] = value
end

#apply_client_update(control_or_id, props) ⇒ Object



1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
# File 'lib/ruflet_ui/ruflet/page.rb', line 1230

def apply_client_update(control_or_id, props)
  control = resolve_control(control_or_id)
  return self unless control

  patch = normalize_props(props || {})
  patch.each { |k, v| control.props[k] = v }

  remove_dialog_tracking(control) if patch.key?("open") && patch["open"] == false

  self
end

#audio(**props) ⇒ Object



358
359
360
# File 'lib/ruflet_ui/ruflet/page.rb', line 358

def audio(**props)
  service(:audio, **props)
end

#audio_recorder(**props) ⇒ Object



362
363
364
# File 'lib/ruflet_ui/ruflet/page.rb', line 362

def audio_recorder(**props)
  service(:audio_recorder, **props)
end

#auto_scrollObject



516
517
518
# File 'lib/ruflet_ui/ruflet/page.rb', line 516

def auto_scroll
  @view_props["auto_scroll"]
end

#auto_scroll=(value) ⇒ Object



520
521
522
# File 'lib/ruflet_ui/ruflet/page.rb', line 520

def auto_scroll=(value)
  @view_props["auto_scroll"] = value
end

#barometer(**props) ⇒ Object



963
964
965
# File 'lib/ruflet_ui/ruflet/page.rb', line 963

def barometer(**props)
  service(:barometer, **props)
end

#battery(**props) ⇒ Object



991
992
993
# File 'lib/ruflet_ui/ruflet/page.rb', line 991

def battery(**props)
  service(:battery, **props)
end

#battery_save_mode?(timeout: nil, on_result: nil) ⇒ Boolean

Returns:

  • (Boolean)


943
944
945
# File 'lib/ruflet_ui/ruflet/page.rb', line 943

def battery_save_mode?(timeout: nil, on_result: nil)
  is_in_battery_save_mode(timeout: timeout, on_result: on_result)
end

#bgcolorObject



252
253
254
# File 'lib/ruflet_ui/ruflet/page.rb', line 252

def bgcolor
  @view_props["bgcolor"]
end

#bgcolor=(value) ⇒ Object



256
257
258
# File 'lib/ruflet_ui/ruflet/page.rb', line 256

def bgcolor=(value)
  @view_props["bgcolor"] = normalize_value("bgcolor", value)
end

#bottom_appbarObject



488
489
490
# File 'lib/ruflet_ui/ruflet/page.rb', line 488

def bottom_appbar
  @view_props["bottom_appbar"]
end

#bottom_appbar=(value) ⇒ Object



492
493
494
# File 'lib/ruflet_ui/ruflet/page.rb', line 492

def bottom_appbar=(value)
  @view_props["bottom_appbar"] = value
end

#bottom_sheetObject



616
617
618
# File 'lib/ruflet_ui/ruflet/page.rb', line 616

def bottom_sheet
  @bottom_sheet
end

#bottom_sheet=(value) ⇒ Object



610
611
612
613
614
# File 'lib/ruflet_ui/ruflet/page.rb', line 610

def bottom_sheet=(value)
  @bottom_sheet = value
  refresh_dialogs_container!
  push_dialogs_update! if @dialogs_container_mounted
end

#bottomappbar=(value) ⇒ Object



496
497
498
# File 'lib/ruflet_ui/ruflet/page.rb', line 496

def bottomappbar=(value)
  self.bottom_appbar = value
end

#bottomsheet=(value) ⇒ Object



620
621
622
# File 'lib/ruflet_ui/ruflet/page.rb', line 620

def bottomsheet=(value)
  self.bottom_sheet = value
end

#browser_context_menuObject



524
525
526
# File 'lib/ruflet_ui/ruflet/page.rb', line 524

def browser_context_menu
  @view_props["browser_context_menu"]
end

#browser_context_menu=(value) ⇒ Object



528
529
530
# File 'lib/ruflet_ui/ruflet/page.rb', line 528

def browser_context_menu=(value)
  @view_props["browser_context_menu"] = value
end

#camera(**props) ⇒ Object



1019
1020
1021
# File 'lib/ruflet_ui/ruflet/page.rb', line 1019

def camera(**props)
  service(:camera, **props)
end

#can_launch_url(url, timeout: 10) ⇒ Object



763
764
765
766
# File 'lib/ruflet_ui/ruflet/page.rb', line 763

def can_launch_url(url, timeout: 10)
  url_launcher = ensure_url_launcher_service
  invoke(url_launcher, "can_launch_url", args: { "url" => url }, timeout: timeout)
end

#cleanObject



298
299
300
301
# File 'lib/ruflet_ui/ruflet/page.rb', line 298

def clean
  replace_root_controls([])
  self
end

#clipboard(**props) ⇒ Object



999
1000
1001
# File 'lib/ruflet_ui/ruflet/page.rb', line 999

def clipboard(**props)
  service(:clipboard, **props)
end

#close_banner(banner_control = nil) ⇒ Object



653
654
655
# File 'lib/ruflet_ui/ruflet/page.rb', line 653

def close_banner(banner_control = nil)
  close_dialog(banner_control)
end

#close_dialog(dialog_control = nil) ⇒ Object



657
658
659
660
661
662
663
664
665
666
667
668
# File 'lib/ruflet_ui/ruflet/page.rb', line 657

def close_dialog(dialog_control = nil)
  target = dialog_control || latest_open_dialog
  return nil unless target

  target.props["open"] = false
  @dialog = nil if @dialog.equal?(target)
  @snack_bar = nil if @snack_bar.equal?(target)
  @bottom_sheet = nil if @bottom_sheet.equal?(target)
  refresh_dialogs_container!
  push_dialogs_update!
  target
end

#close_drawer(timeout: 10, on_result: nil) ⇒ Object



676
677
678
# File 'lib/ruflet_ui/ruflet/page.rb', line 676

def close_drawer(timeout: 10, on_result: nil)
  invoke(:page, "close_drawer", timeout: timeout, on_result: on_result)
end

#close_end_drawer(timeout: 10, on_result: nil) ⇒ Object



686
687
688
# File 'lib/ruflet_ui/ruflet/page.rb', line 686

def close_end_drawer(timeout: 10, on_result: nil)
  invoke(:page, "close_end_drawer", timeout: timeout, on_result: on_result)
end

#close_in_app_web_view(timeout: 10, on_result: nil) ⇒ Object



768
769
770
771
# File 'lib/ruflet_ui/ruflet/page.rb', line 768

def close_in_app_web_view(timeout: 10, on_result: nil)
  url_launcher = ensure_url_launcher_service
  invoke(url_launcher, "close_in_app_web_view", timeout: timeout, on_result: on_result)
end

#connectivity(**props) ⇒ Object



995
996
997
# File 'lib/ruflet_ui/ruflet/page.rb', line 995

def connectivity(**props)
  service(:connectivity, **props)
end

#controlsObject



271
272
273
# File 'lib/ruflet_ui/ruflet/page.rb', line 271

def controls
  @root_controls
end

#controls=(value) ⇒ Object



275
276
277
278
# File 'lib/ruflet_ui/ruflet/page.rb', line 275

def controls=(value)
  replace_root_controls(Array(value).flatten.compact)
  self
end

#decorationObject



532
533
534
# File 'lib/ruflet_ui/ruflet/page.rb', line 532

def decoration
  @view_props["decoration"]
end

#decoration=(value) ⇒ Object



536
537
538
# File 'lib/ruflet_ui/ruflet/page.rb', line 536

def decoration=(value)
  @view_props["decoration"] = value
end

#dialogObject



588
# File 'lib/ruflet_ui/ruflet/page.rb', line 588

def dialog = @dialog

#dialog=(value) ⇒ Object



590
591
592
593
594
# File 'lib/ruflet_ui/ruflet/page.rb', line 590

def dialog=(value)
  @dialog = value
  refresh_dialogs_container!
  push_dialogs_update! if @dialogs_container_mounted
end

#dispatch_event(target:, name:, data:) ⇒ Object



1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
# File 'lib/ruflet_ui/ruflet/page.rb', line 1242

def dispatch_event(target:, name:, data:)
  if page_control_target?(target)
    if name.to_s == "route_change"
      route_from_event = extract_route(data)
      return if route_from_event && route_from_event == @page_props["route"]
      @page_props["route"] = route_from_event if route_from_event
    end
    dispatch_page_event(name: name, data: data)
    return
  end

  control = @wire_index[target.to_i] || @control_index[target.to_s]
  return unless control

  event = Event.new(name: name, target: target, raw_data: data, page: self, control: control)
  apply_event_value_to_control(control, event) if %w[change select select_change].include?(name.to_s)
  control.emit(name, event)

  if name.to_s == "dismiss" && remove_dialog_tracking(control)
    push_dialogs_update!
  end
end

#drawerObject



572
573
574
# File 'lib/ruflet_ui/ruflet/page.rb', line 572

def drawer
  @view_props["drawer"]
end

#drawer=(value) ⇒ Object



576
577
578
# File 'lib/ruflet_ui/ruflet/page.rb', line 576

def drawer=(value)
  @view_props["drawer"] = value
end

#end_drawerObject



580
581
582
# File 'lib/ruflet_ui/ruflet/page.rb', line 580

def end_drawer
  @view_props["end_drawer"]
end

#end_drawer=(value) ⇒ Object



584
585
586
# File 'lib/ruflet_ui/ruflet/page.rb', line 584

def end_drawer=(value)
  @view_props["end_drawer"] = value
end

#file_picker(**props) ⇒ Object



1003
1004
1005
# File 'lib/ruflet_ui/ruflet/page.rb', line 1003

def file_picker(**props)
  service(:file_picker, **props)
end

#flashlight(**props) ⇒ Object



346
347
348
349
350
# File 'lib/ruflet_ui/ruflet/page.rb', line 346

def flashlight(**props)
  return service(:flashlight, **props) unless props.empty?

  @flashlight_proxy
end

#floating_action_buttonObject



500
501
502
# File 'lib/ruflet_ui/ruflet/page.rb', line 500

def floating_action_button
  @view_props["floating_action_button"]
end

#floating_action_button=(value) ⇒ Object



504
505
506
# File 'lib/ruflet_ui/ruflet/page.rb', line 504

def floating_action_button=(value)
  @view_props["floating_action_button"] = value
end

#floating_action_button_locationObject



540
541
542
# File 'lib/ruflet_ui/ruflet/page.rb', line 540

def floating_action_button_location
  @view_props["floating_action_button_location"]
end

#floating_action_button_location=(value) ⇒ Object



544
545
546
# File 'lib/ruflet_ui/ruflet/page.rb', line 544

def floating_action_button_location=(value)
  @view_props["floating_action_button_location"] = value
end

#foreground_decorationObject



548
549
550
# File 'lib/ruflet_ui/ruflet/page.rb', line 548

def foreground_decoration
  @view_props["foreground_decoration"]
end

#foreground_decoration=(value) ⇒ Object



552
553
554
# File 'lib/ruflet_ui/ruflet/page.rb', line 552

def foreground_decoration=(value)
  @view_props["foreground_decoration"] = value
end

#geolocator(**props) ⇒ Object



967
968
969
# File 'lib/ruflet_ui/ruflet/page.rb', line 967

def geolocator(**props)
  service(:geolocator, **props)
end

#get_application_cache_directory(timeout: nil, on_result: nil) ⇒ Object



1027
1028
1029
# File 'lib/ruflet_ui/ruflet/page.rb', line 1027

def get_application_cache_directory(timeout: nil, on_result: nil)
  invoke_storage_paths("get_application_cache_directory", timeout: timeout, on_result: on_result)
end

#get_application_documents_directory(timeout: nil, on_result: nil) ⇒ Object



1031
1032
1033
# File 'lib/ruflet_ui/ruflet/page.rb', line 1031

def get_application_documents_directory(timeout: nil, on_result: nil)
  invoke_storage_paths("get_application_documents_directory", timeout: timeout, on_result: on_result)
end

#get_application_support_directory(timeout: nil, on_result: nil) ⇒ Object



1035
1036
1037
# File 'lib/ruflet_ui/ruflet/page.rb', line 1035

def get_application_support_directory(timeout: nil, on_result: nil)
  invoke_storage_paths("get_application_support_directory", timeout: timeout, on_result: on_result)
end

#get_battery_level(timeout: nil, on_result: nil) ⇒ Object



931
932
933
# File 'lib/ruflet_ui/ruflet/page.rb', line 931

def get_battery_level(timeout: nil, on_result: nil)
  invoke_battery_method("get_battery_level", timeout: timeout, on_result: on_result)
end

#get_battery_state(timeout: nil, on_result: nil) ⇒ Object



935
936
937
# File 'lib/ruflet_ui/ruflet/page.rb', line 935

def get_battery_state(timeout: nil, on_result: nil)
  invoke_battery_method("get_battery_state", timeout: timeout, on_result: on_result)
end

#get_clipboard(timeout: nil, on_result: nil) ⇒ Object



897
898
899
# File 'lib/ruflet_ui/ruflet/page.rb', line 897

def get_clipboard(timeout: nil, on_result: nil)
  invoke_clipboard_method("get", timeout: timeout, on_result: on_result)
end

#get_clipboard_files(timeout: nil, on_result: nil) ⇒ Object



910
911
912
# File 'lib/ruflet_ui/ruflet/page.rb', line 910

def get_clipboard_files(timeout: nil, on_result: nil)
  invoke_clipboard_method("get_files", timeout: timeout, on_result: on_result)
end

#get_clipboard_image(timeout: nil, on_result: nil) ⇒ Object



923
924
925
# File 'lib/ruflet_ui/ruflet/page.rb', line 923

def get_clipboard_image(timeout: nil, on_result: nil)
  invoke_clipboard_method("get_image", timeout: timeout, on_result: on_result)
end

#get_connectivity(timeout: nil, on_result: nil) ⇒ Object



927
928
929
# File 'lib/ruflet_ui/ruflet/page.rb', line 927

def get_connectivity(timeout: nil, on_result: nil)
  invoke_connectivity_method("get_connectivity", timeout: timeout, on_result: on_result)
end

#get_console_log_filename(timeout: nil, on_result: nil) ⇒ Object



1063
1064
1065
# File 'lib/ruflet_ui/ruflet/page.rb', line 1063

def get_console_log_filename(timeout: nil, on_result: nil)
  invoke_storage_paths("get_console_log_filename", timeout: timeout, on_result: on_result)
end

#get_control(id) ⇒ Object



313
314
315
316
# File 'lib/ruflet_ui/ruflet/page.rb', line 313

def get_control(id)
  refresh_control_indexes!
  resolve_control(id)
end

#get_directory_path(dialog_title: nil, initial_directory: nil, timeout: nil, on_result: nil) ⇒ Object



843
844
845
846
847
848
849
850
851
852
853
# File 'lib/ruflet_ui/ruflet/page.rb', line 843

def get_directory_path(dialog_title: nil, initial_directory: nil, timeout: nil, on_result: nil)
  invoke_file_picker(
    "get_directory_path",
    compact_service_args(
      "dialog_title" => dialog_title,
      "initial_directory" => initial_directory
    ),
    timeout: timeout,
    on_result: on_result
  )
end

#get_downloads_directory(timeout: nil, on_result: nil) ⇒ Object



1039
1040
1041
# File 'lib/ruflet_ui/ruflet/page.rb', line 1039

def get_downloads_directory(timeout: nil, on_result: nil)
  invoke_storage_paths("get_downloads_directory", timeout: timeout, on_result: on_result)
end

#get_external_cache_directories(timeout: nil, on_result: nil) ⇒ Object



1043
1044
1045
# File 'lib/ruflet_ui/ruflet/page.rb', line 1043

def get_external_cache_directories(timeout: nil, on_result: nil)
  invoke_storage_paths("get_external_cache_directories", timeout: timeout, on_result: on_result)
end

#get_external_storage_directories(timeout: nil, on_result: nil) ⇒ Object



1047
1048
1049
# File 'lib/ruflet_ui/ruflet/page.rb', line 1047

def get_external_storage_directories(timeout: nil, on_result: nil)
  invoke_storage_paths("get_external_storage_directories", timeout: timeout, on_result: on_result)
end

#get_external_storage_directory(timeout: nil, on_result: nil) ⇒ Object



1055
1056
1057
# File 'lib/ruflet_ui/ruflet/page.rb', line 1055

def get_external_storage_directory(timeout: nil, on_result: nil)
  invoke_storage_paths("get_external_storage_directory", timeout: timeout, on_result: on_result)
end

#get_library_directory(timeout: nil, on_result: nil) ⇒ Object



1051
1052
1053
# File 'lib/ruflet_ui/ruflet/page.rb', line 1051

def get_library_directory(timeout: nil, on_result: nil)
  invoke_storage_paths("get_library_directory", timeout: timeout, on_result: on_result)
end

#get_temporary_directory(timeout: nil, on_result: nil) ⇒ Object



1059
1060
1061
# File 'lib/ruflet_ui/ruflet/page.rb', line 1059

def get_temporary_directory(timeout: nil, on_result: nil)
  invoke_storage_paths("get_temporary_directory", timeout: timeout, on_result: on_result)
end

#go(route, **query_params) ⇒ Object



436
437
438
439
440
441
# File 'lib/ruflet_ui/ruflet/page.rb', line 436

def go(route, **query_params)
  @page_props["route"] = build_route(route, query_params)
  dispatch_page_event(name: "route_change", data: @page_props["route"])
  send_view_patch
  self
end

#gyroscope(**props) ⇒ Object



951
952
953
# File 'lib/ruflet_ui/ruflet/page.rb', line 951

def gyroscope(**props)
  service(:gyroscope, **props)
end

#handle_invoke_method_result(payload) ⇒ Object



1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
# File 'lib/ruflet_ui/ruflet/page.rb', line 1152

def handle_invoke_method_result(payload)
  call_id = payload["call_id"].to_s
  waiter = @invoke_waiters_mutex.synchronize { @invoke_waiters[call_id] }
  if waiter
    waiter << payload
    return true
  end

  callback = @invoke_waiters_mutex.synchronize { @invoke_callbacks.delete(call_id) }
  return false unless callback

  callback.call(payload["result"], payload["error"])
  true
rescue StandardError => e
  Kernel.warn("invoke callback error: #{e.class}: #{e.message}")
  false
end

#haptic_feedback(**props) ⇒ Object



1023
1024
1025
# File 'lib/ruflet_ui/ruflet/page.rb', line 1023

def haptic_feedback(**props)
  service(:haptic_feedback, **props)
end

#heavy_impact(timeout: 10, on_result: nil) ⇒ Object



868
869
870
# File 'lib/ruflet_ui/ruflet/page.rb', line 868

def heavy_impact(timeout: 10, on_result: nil)
  invoke_haptic_feedback("heavy_impact", timeout: timeout, on_result: on_result)
end

#horizontal_alignmentObject



242
243
244
# File 'lib/ruflet_ui/ruflet/page.rb', line 242

def horizontal_alignment
  @page_props["horizontal_alignment"] || @view_props["horizontal_alignment"]
end

#horizontal_alignment=(value) ⇒ Object



246
247
248
249
250
# File 'lib/ruflet_ui/ruflet/page.rb', line 246

def horizontal_alignment=(value)
  v = normalize_value("horizontal_alignment", value)
  @page_props["horizontal_alignment"] = v
  @view_props["horizontal_alignment"] = v
end

#insert(at, *controls) ⇒ Object



280
281
282
283
284
# File 'lib/ruflet_ui/ruflet/page.rb', line 280

def insert(at, *controls)
  @root_controls.insert(at.to_i, *controls.flatten.compact)
  send_view_patch
  self
end

#invoke(control_or_id, method_name, args: nil, timeout: 10, on_result: nil) ⇒ Object



706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
# File 'lib/ruflet_ui/ruflet/page.rb', line 706

def invoke(control_or_id, method_name, args: nil, timeout: 10, on_result: nil)
  control_id =
    if page_control_target?(control_or_id)
      1
    else
      control = resolve_control(control_or_id)
      return nil unless control
      control.wire_id
    end

  call_id = "call_#{Ruflet::Control.generate_id}"
  if on_result.respond_to?(:call)
    @invoke_waiters_mutex.synchronize { @invoke_callbacks[call_id] = on_result }
    unless timeout.nil?
      Thread.new(call_id, timeout.to_f) do |pending_call_id, invoke_timeout|
        sleep([invoke_timeout, 0.0].max + 0.1)
        callback = @invoke_waiters_mutex.synchronize { @invoke_callbacks.delete(pending_call_id) }
        callback&.call(nil, "execution expired")
      rescue StandardError => e
        Kernel.warn("invoke timeout callback error: #{e.class}: #{e.message}")
      end
    end
  end
  payload = {
    "control_id" => control_id,
    "call_id" => call_id,
    "name" => method_name.to_s,
    "args" => args
  }
  payload["timeout"] = timeout unless timeout.nil?
  send_message(Protocol::ACTIONS[:invoke_control_method], payload)

  call_id
end

#invoke_sync(control_or_id, method_name, args: nil, timeout: 10) ⇒ Object

Synchronous invoke for controls/services that must return a value before continuing (e.g. picker selection, camera discovery/init).



743
744
745
# File 'lib/ruflet_ui/ruflet/page.rb', line 743

def invoke_sync(control_or_id, method_name, args: nil, timeout: 10)
  invoke_and_wait(control_or_id, method_name, args: args, timeout: timeout)
end

#is_in_battery_save_mode(timeout: nil, on_result: nil) ⇒ Object



939
940
941
# File 'lib/ruflet_ui/ruflet/page.rb', line 939

def is_in_battery_save_mode(timeout: nil, on_result: nil)
  invoke_battery_method("is_in_battery_save_mode", timeout: timeout, on_result: on_result)
end

#launch_url(url, mode: nil, web_view_configuration: nil, browser_configuration: nil, web_only_window_name: nil, timeout: 10, on_result: nil) ⇒ Object



747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
# File 'lib/ruflet_ui/ruflet/page.rb', line 747

def launch_url(url, mode: nil, web_view_configuration: nil, browser_configuration: nil, web_only_window_name: nil, timeout: 10, on_result: nil)
  url_launcher = ensure_url_launcher_service
  args = { "url" => url }
  args["mode"] = mode unless mode.nil?
  args["web_view_configuration"] = web_view_configuration unless web_view_configuration.nil?
  args["browser_configuration"] = browser_configuration unless browser_configuration.nil?
  args["web_only_window_name"] = web_only_window_name unless web_only_window_name.nil?
  invoke(
    url_launcher,
    "launch_url",
    args: args,
    timeout: timeout,
    on_result: on_result
  )
end

#light_impact(timeout: 10, on_result: nil) ⇒ Object



876
877
878
# File 'lib/ruflet_ui/ruflet/page.rb', line 876

def light_impact(timeout: 10, on_result: nil)
  invoke_haptic_feedback("light_impact", timeout: timeout, on_result: on_result)
end

#magnetometer(**props) ⇒ Object



959
960
961
# File 'lib/ruflet_ui/ruflet/page.rb', line 959

def magnetometer(**props)
  service(:magnetometer, **props)
end

#medium_impact(timeout: 10, on_result: nil) ⇒ Object



872
873
874
# File 'lib/ruflet_ui/ruflet/page.rb', line 872

def medium_impact(timeout: 10, on_result: nil)
  invoke_haptic_feedback("medium_impact", timeout: timeout, on_result: on_result)
end

#mount(&block) ⇒ Object



474
475
476
477
478
# File 'lib/ruflet_ui/ruflet/page.rb', line 474

def mount(&block)
  builder = WidgetBuilder.new
  builder.instance_eval(&block)
  builder.children.each { |control| add(control) }
end


443
444
445
# File 'lib/ruflet_ui/ruflet/page.rb', line 443

def navigate(route, **query_params)
  go(route, **query_params)
end


508
509
510
# File 'lib/ruflet_ui/ruflet/page.rb', line 508

def navigation_bar
  @view_props["navigation_bar"]
end


512
513
514
# File 'lib/ruflet_ui/ruflet/page.rb', line 512

def navigation_bar=(value)
  @view_props["navigation_bar"] = value
end

#on(event_name, &block) ⇒ Object



467
468
469
470
471
472
# File 'lib/ruflet_ui/ruflet/page.rb', line 467

def on(event_name, &block)
  name = event_name.to_s
  name = name[3..-1] if name.start_with?("on_")
  @page_event_handlers[name] = block
  self
end

#on_route_change=(handler) ⇒ Object



455
456
457
# File 'lib/ruflet_ui/ruflet/page.rb', line 455

def on_route_change=(handler)
  @page_event_handlers["route_change"] = handler
end

#on_view_pop=(handler) ⇒ Object



459
460
461
# File 'lib/ruflet_ui/ruflet/page.rb', line 459

def on_view_pop=(handler)
  @page_event_handlers["view_pop"] = handler
end

#on_views_pop_until=(handler) ⇒ Object



463
464
465
# File 'lib/ruflet_ui/ruflet/page.rb', line 463

def on_views_pop_until=(handler)
  @page_event_handlers["views_pop_until"] = handler
end

#open_window(url, title: nil, width: nil, height: nil, timeout: 10, on_result: nil) ⇒ Object



773
774
775
776
777
778
779
780
# File 'lib/ruflet_ui/ruflet/page.rb', line 773

def open_window(url, title: nil, width: nil, height: nil, timeout: 10, on_result: nil)
  url_launcher = ensure_url_launcher_service
  args = { "url" => url }
  args["title"] = title unless title.nil?
  args["width"] = width unless width.nil?
  args["height"] = height unless height.nil?
  invoke(url_launcher, "open_window", args: args, timeout: timeout, on_result: on_result)
end

#overlayObject



303
304
305
# File 'lib/ruflet_ui/ruflet/page.rb', line 303

def overlay
  @overlay_container.children
end

#overlay=(value) ⇒ Object



307
308
309
310
311
# File 'lib/ruflet_ui/ruflet/page.rb', line 307

def overlay=(value)
  @overlay_container.children.replace(Array(value).flatten.compact)
  push_overlay_update!
  self
end

#paddingObject



556
557
558
# File 'lib/ruflet_ui/ruflet/page.rb', line 556

def padding
  @view_props["padding"]
end

#padding=(value) ⇒ Object



560
561
562
# File 'lib/ruflet_ui/ruflet/page.rb', line 560

def padding=(value)
  @view_props["padding"] = value
end

#patch_page(control_id, **props) ⇒ Object



1226
1227
1228
# File 'lib/ruflet_ui/ruflet/page.rb', line 1226

def patch_page(control_id, **props)
  update(control_id, **props)
end

#permission_handler(**props) ⇒ Object



971
972
973
# File 'lib/ruflet_ui/ruflet/page.rb', line 971

def permission_handler(**props)
  service(:permission_handler, **props)
end

#pick_files(dialog_title: nil, initial_directory: nil, file_type: "any", allowed_extensions: nil, allow_multiple: false, with_data: false, timeout: nil, on_result: nil) ⇒ Object

File picker helpers: create an ephemeral service, invoke method, and dispose it.



793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
# File 'lib/ruflet_ui/ruflet/page.rb', line 793

def pick_files(
  dialog_title: nil,
  initial_directory: nil,
  file_type: "any",
  allowed_extensions: nil,
  allow_multiple: false,
  with_data: false,
  timeout: nil,
  on_result: nil
)
  invoke_file_picker(
    "pick_files",
    compact_service_args(
      "dialog_title" => dialog_title,
      "initial_directory" => initial_directory,
      "file_type" => file_type,
      "allowed_extensions" => allowed_extensions,
      "allow_multiple" => allow_multiple,
      "with_data" => with_data
    ),
    timeout: timeout,
    on_result: on_result
  )
end

#pop_dialogObject



1170
1171
1172
# File 'lib/ruflet_ui/ruflet/page.rb', line 1170

def pop_dialog
  close_dialog
end

#push_route(route, **query_params) ⇒ Object



447
448
449
# File 'lib/ruflet_ui/ruflet/page.rb', line 447

def push_route(route, **query_params)
  go(route, **query_params)
end

#queryObject



451
452
453
# File 'lib/ruflet_ui/ruflet/page.rb', line 451

def query
  parse_query(route)
end

#remove(*controls) ⇒ Object



286
287
288
289
290
# File 'lib/ruflet_ui/ruflet/page.rb', line 286

def remove(*controls)
  controls.flatten.each { |control| @root_controls.delete(control) }
  send_view_patch
  self
end

#remove_at(index) ⇒ Object



292
293
294
295
296
# File 'lib/ruflet_ui/ruflet/page.rb', line 292

def remove_at(index)
  @root_controls.delete_at(index.to_i)
  send_view_patch
  self
end

#remove_service(*value) ⇒ Object



373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
# File 'lib/ruflet_ui/ruflet/page.rb', line 373

def remove_service(*value)
  targets = value.flatten.compact
  return self if targets.empty?

  @services_container.props["_services"] = services.reject do |service|
    targets.any? do |target|
      case target
      when Control
        service.equal?(target) || (!target.id.nil? && service.id.to_s == target.id.to_s)
      else
        needle = target.to_s
        service.id.to_s == needle || service.type.to_s.downcase == needle.downcase
      end
    end
  end

  refresh_services_container!
  push_services_update!
  self
end

#respond_to_missing?(name, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
# File 'lib/ruflet_ui/ruflet/page.rb', line 1293

def respond_to_missing?(name, include_private = false)
  method_name = name.to_s
  prop_name = method_name.delete_suffix("=")
  widget_helper_method?(name) ||
    widget_helper_method?(prop_name) ||
    method_name.end_with?("=") ||
    @page_props.key?(method_name) ||
    @view_props.key?(method_name) ||
    DIALOG_PROP_KEYS.include?(method_name) ||
    super
end

#routeObject



224
225
226
# File 'lib/ruflet_ui/ruflet/page.rb', line 224

def route
  @page_props["route"]
end

#route=(value) ⇒ Object



228
229
230
# File 'lib/ruflet_ui/ruflet/page.rb', line 228

def route=(value)
  @page_props["route"] = value
end

#save_file(dialog_title: nil, file_name: nil, initial_directory: nil, file_type: "any", allowed_extensions: nil, src_bytes: nil, timeout: nil, on_result: nil) ⇒ Object



818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
# File 'lib/ruflet_ui/ruflet/page.rb', line 818

def save_file(
  dialog_title: nil,
  file_name: nil,
  initial_directory: nil,
  file_type: "any",
  allowed_extensions: nil,
  src_bytes: nil,
  timeout: nil,
  on_result: nil
)
  invoke_file_picker(
    "save_file",
    compact_service_args(
      "dialog_title" => dialog_title,
      "file_name" => file_name,
      "initial_directory" => initial_directory,
      "file_type" => file_type,
      "allowed_extensions" => allowed_extensions,
      "src_bytes" => src_bytes
    ),
    timeout: timeout,
    on_result: on_result
  )
end

#schedule_updateObject



1222
1223
1224
# File 'lib/ruflet_ui/ruflet/page.rb', line 1222

def schedule_update
  update
end

#screen_brightness(**props) ⇒ Object



352
353
354
355
356
# File 'lib/ruflet_ui/ruflet/page.rb', line 352

def screen_brightness(**props)
  return service(:screen_brightness, **props) unless props.empty?

  @screen_brightness_proxy
end

#screenshot(**props) ⇒ Object



987
988
989
# File 'lib/ruflet_ui/ruflet/page.rb', line 987

def screenshot(**props)
  Ruflet::UI::ControlFactory.build(:screenshot, **props)
end

#scroll_to(offset: nil, delta: nil, scroll_key: nil, duration: nil, curve: nil, timeout: 10, on_result: nil) ⇒ Object



690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
# File 'lib/ruflet_ui/ruflet/page.rb', line 690

def scroll_to(offset: nil, delta: nil, scroll_key: nil, duration: nil, curve: nil, timeout: 10, on_result: nil)
  invoke(
    :page,
    "scroll_to",
    args: {
      "offset" => offset,
      "delta" => delta,
      "scroll_key" => scroll_key,
      "duration" => duration,
      "curve" => curve
    },
    timeout: timeout,
    on_result: on_result
  )
end

#secure_storage(**props) ⇒ Object



975
976
977
# File 'lib/ruflet_ui/ruflet/page.rb', line 975

def secure_storage(**props)
  service(:secure_storage, **props)
end

#selection_click(timeout: 10, on_result: nil) ⇒ Object



880
881
882
# File 'lib/ruflet_ui/ruflet/page.rb', line 880

def selection_click(timeout: 10, on_result: nil)
  invoke_haptic_feedback("selection_click", timeout: timeout, on_result: on_result)
end

#semantics_service(**props) ⇒ Object



983
984
985
# File 'lib/ruflet_ui/ruflet/page.rb', line 983

def semantics_service(**props)
  service(:semantics_service, **props)
end

#service(type, **props) ⇒ Object



394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
# File 'lib/ruflet_ui/ruflet/page.rb', line 394

def service(type, **props)
  mapped_props = normalize_props(props || {})
  id = mapped_props.delete("id")
  normalized_type = type.to_s.downcase
  compact_type = normalized_type.delete("_")

  if visual_service_type?(normalized_type)
    key = id ? "id:#{id}" : normalized_type
    existing = @visual_service_controls[key]
    return existing if existing

    svc = Ruflet::UI::ControlFactory.build(type.to_s, id: id&.to_s, **mapped_props)
    @visual_service_controls[key] = svc
    return svc
  end

  unless service_control_type?(normalized_type)
    raise ArgumentError, "#{type} is a visual control, not a page service"
  end

  existing =
    if id
      services.find { |s| s.is_a?(Control) && s.id.to_s == id.to_s }
    else
      services.find do |s|
        s.is_a?(Control) && s.type.to_s.downcase.delete("_") == compact_type
      end
    end
  if existing
    unless mapped_props.empty?
      existing.merge_props(mapped_props)
      refresh_services_container!
      push_services_update!
    end
    return existing
  end

  svc = Ruflet::UI::ControlFactory.build(type.to_s, id: id&.to_s, **mapped_props)
  add_service(svc) unless services.include?(svc)
  svc
end

#servicesObject



323
324
325
# File 'lib/ruflet_ui/ruflet/page.rb', line 323

def services
  @services_container.props["_services"] ||= []
end

#services=(value) ⇒ Object



327
328
329
330
331
332
# File 'lib/ruflet_ui/ruflet/page.rb', line 327

def services=(value)
  @services_container.props["_services"] = Array(value).compact
  refresh_services_container!
  push_services_update!
  self
end

#set_clipboard(value, timeout: nil, on_result: nil) ⇒ Object



888
889
890
891
892
893
894
895
# File 'lib/ruflet_ui/ruflet/page.rb', line 888

def set_clipboard(value, timeout: nil, on_result: nil)
  invoke_clipboard_method(
    "set",
    args: { "data" => value.to_s },
    timeout: timeout,
    on_result: on_result
  )
end

#set_clipboard_files(files, timeout: nil, on_result: nil) ⇒ Object



901
902
903
904
905
906
907
908
# File 'lib/ruflet_ui/ruflet/page.rb', line 901

def set_clipboard_files(files, timeout: nil, on_result: nil)
  invoke_clipboard_method(
    "set_files",
    args: { "files" => Array(files).map(&:to_s) },
    timeout: timeout,
    on_result: on_result
  )
end

#set_clipboard_image(value, timeout: nil, on_result: nil) ⇒ Object



914
915
916
917
918
919
920
921
# File 'lib/ruflet_ui/ruflet/page.rb', line 914

def set_clipboard_image(value, timeout: nil, on_result: nil)
  invoke_clipboard_method(
    "set_image",
    args: { "data" => value },
    timeout: timeout,
    on_result: on_result
  )
end

#set_view_props(props) ⇒ Object



211
212
213
214
# File 'lib/ruflet_ui/ruflet/page.rb', line 211

def set_view_props(props)
  split_props(normalize_props(props || {}))
  self
end

#shake_detector(**props) ⇒ Object



979
980
981
# File 'lib/ruflet_ui/ruflet/page.rb', line 979

def shake_detector(**props)
  service(:shake_detector, **props)
end

#share(**props) ⇒ Object



1015
1016
1017
# File 'lib/ruflet_ui/ruflet/page.rb', line 1015

def share(**props)
  service(:share, **props)
end

#share_files(files = nil, text: nil, title: nil, subject: nil, preview_thumbnail: nil, share_position_origin: nil, download_fallback_enabled: true, mail_to_fallback_enabled: true, excluded_cupertino_activities: nil, timeout: nil, on_result: nil) ⇒ Object



1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
# File 'lib/ruflet_ui/ruflet/page.rb', line 1119

def share_files(
  files = nil,
  text: nil,
  title: nil,
  subject: nil,
  preview_thumbnail: nil,
  share_position_origin: nil,
  download_fallback_enabled: true,
  mail_to_fallback_enabled: true,
  excluded_cupertino_activities: nil,
  timeout: nil,
  on_result: nil
)
  share = ensure_share_service
  invoke(
    share,
    "share_files",
    args: compact_service_args(
      "files" => normalize_share_files(files),
      "text" => text,
      "title" => title,
      "subject" => subject,
      "preview_thumbnail" => normalize_share_file(preview_thumbnail),
      "share_position_origin" => share_position_origin,
      "download_fallback_enabled" => download_fallback_enabled,
      "mail_to_fallback_enabled" => mail_to_fallback_enabled,
      "excluded_cupertino_activities" => excluded_cupertino_activities
    ),
    timeout: timeout,
    on_result: on_result
  )
end

#share_text(text = nil, title: nil, subject: nil, preview_thumbnail: nil, share_position_origin: nil, download_fallback_enabled: true, mail_to_fallback_enabled: true, excluded_cupertino_activities: nil, timeout: nil, on_result: nil) ⇒ Object



1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
# File 'lib/ruflet_ui/ruflet/page.rb', line 1067

def share_text(
  text = nil,
  title: nil,
  subject: nil,
  preview_thumbnail: nil,
  share_position_origin: nil,
  download_fallback_enabled: true,
  mail_to_fallback_enabled: true,
  excluded_cupertino_activities: nil,
  timeout: nil,
  on_result: nil
)
  share = ensure_share_service
  invoke(
    share,
    "share_text",
    args: compact_service_args(
      "text" => text,
      "title" => title,
      "subject" => subject,
      "preview_thumbnail" => preview_thumbnail,
      "share_position_origin" => share_position_origin,
      "download_fallback_enabled" => download_fallback_enabled,
      "mail_to_fallback_enabled" => mail_to_fallback_enabled,
      "excluded_cupertino_activities" => excluded_cupertino_activities
    ),
    timeout: timeout,
    on_result: on_result
  )
end

#share_uri(uri = nil, share_position_origin: nil, excluded_cupertino_activities: nil, timeout: nil, on_result: nil) ⇒ Object



1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
# File 'lib/ruflet_ui/ruflet/page.rb', line 1098

def share_uri(
  uri = nil,
  share_position_origin: nil,
  excluded_cupertino_activities: nil,
  timeout: nil,
  on_result: nil
)
  share = ensure_share_service
  invoke(
    share,
    "share_uri",
    args: compact_service_args(
      "uri" => uri,
      "share_position_origin" => share_position_origin,
      "excluded_cupertino_activities" => excluded_cupertino_activities
    ),
    timeout: timeout,
    on_result: on_result
  )
end

#shared_preferences(**props) ⇒ Object



334
335
336
337
338
# File 'lib/ruflet_ui/ruflet/page.rb', line 334

def shared_preferences(**props)
  return service(:shared_preferences, **props) unless props.empty?

  @shared_preferences_proxy
end

#show_banner(banner_control) ⇒ Object



649
650
651
# File 'lib/ruflet_ui/ruflet/page.rb', line 649

def show_banner(banner_control)
  show_dialog(banner_control)
end

#show_bottom_sheet(bottom_sheet_control) ⇒ Object



645
646
647
# File 'lib/ruflet_ui/ruflet/page.rb', line 645

def show_bottom_sheet(bottom_sheet_control)
  show_dialog(bottom_sheet_control)
end

#show_dialog(dialog_control) ⇒ Object



624
625
626
627
628
629
630
631
632
633
634
635
# File 'lib/ruflet_ui/ruflet/page.rb', line 624

def show_dialog(dialog_control)
  return self unless dialog_control

  return self if dialog_open?(dialog_control)

  dialog_control.props["open"] = true
  @dialogs << dialog_control unless @dialogs.include?(dialog_control)
  refresh_dialogs_container!
  send_view_patch unless @dialogs_container.wire_id
  push_dialogs_update!
  self
end

#show_drawer(timeout: 10, on_result: nil) ⇒ Object

Raises:

  • (ArgumentError)


670
671
672
673
674
# File 'lib/ruflet_ui/ruflet/page.rb', line 670

def show_drawer(timeout: 10, on_result: nil)
  raise ArgumentError, "No drawer defined" unless drawer

  invoke(:page, "show_drawer", timeout: timeout, on_result: on_result)
end

#show_end_drawer(timeout: 10, on_result: nil) ⇒ Object

Raises:

  • (ArgumentError)


680
681
682
683
684
# File 'lib/ruflet_ui/ruflet/page.rb', line 680

def show_end_drawer(timeout: 10, on_result: nil)
  raise ArgumentError, "No end_drawer defined" unless end_drawer

  invoke(:page, "show_end_drawer", timeout: timeout, on_result: on_result)
end

#show_snack_bar(snack_bar_control) ⇒ Object



637
638
639
# File 'lib/ruflet_ui/ruflet/page.rb', line 637

def show_snack_bar(snack_bar_control)
  show_dialog(snack_bar_control)
end

#show_snackbar(snack_bar_control) ⇒ Object



641
642
643
# File 'lib/ruflet_ui/ruflet/page.rb', line 641

def show_snackbar(snack_bar_control)
  show_dialog(snack_bar_control)
end

#snack_barObject



602
603
604
# File 'lib/ruflet_ui/ruflet/page.rb', line 602

def snack_bar
  @snack_bar
end

#snack_bar=(value) ⇒ Object



596
597
598
599
600
# File 'lib/ruflet_ui/ruflet/page.rb', line 596

def snack_bar=(value)
  @snack_bar = value
  refresh_dialogs_container!
  push_dialogs_update! if @dialogs_container_mounted
end

#snackbar=(value) ⇒ Object



606
607
608
# File 'lib/ruflet_ui/ruflet/page.rb', line 606

def snackbar=(value)
  self.snack_bar = value
end

#spacingObject



564
565
566
# File 'lib/ruflet_ui/ruflet/page.rb', line 564

def spacing
  @view_props["spacing"]
end

#spacing=(value) ⇒ Object



568
569
570
# File 'lib/ruflet_ui/ruflet/page.rb', line 568

def spacing=(value)
  @view_props["spacing"] = value
end

#storage_paths(**props) ⇒ Object



1011
1012
1013
# File 'lib/ruflet_ui/ruflet/page.rb', line 1011

def storage_paths(**props)
  service(:storage_paths, **props)
end

#supports_close_for_launch_mode(mode, timeout: 10, on_result: nil) ⇒ Object



787
788
789
790
# File 'lib/ruflet_ui/ruflet/page.rb', line 787

def supports_close_for_launch_mode(mode, timeout: 10, on_result: nil)
  url_launcher = ensure_url_launcher_service
  invoke(url_launcher, "supports_close_for_launch_mode", args: { "mode" => mode }, timeout: timeout, on_result: on_result)
end

#supports_launch_mode(mode, timeout: 10, on_result: nil) ⇒ Object



782
783
784
785
# File 'lib/ruflet_ui/ruflet/page.rb', line 782

def supports_launch_mode(mode, timeout: 10, on_result: nil)
  url_launcher = ensure_url_launcher_service
  invoke(url_launcher, "supports_launch_mode", args: { "mode" => mode }, timeout: timeout, on_result: on_result)
end

#titleObject



216
217
218
# File 'lib/ruflet_ui/ruflet/page.rb', line 216

def title
  @page_props["title"]
end

#title=(value) ⇒ Object



220
221
222
# File 'lib/ruflet_ui/ruflet/page.rb', line 220

def title=(value)
  @page_props["title"] = value
end

#update(control_or_id = nil, **props) ⇒ Object



1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
# File 'lib/ruflet_ui/ruflet/page.rb', line 1174

def update(control_or_id = nil, **props)
  if control_or_id.nil? && props.empty?
    send_view_patch
    return self
  end

  if page_control_target?(control_or_id)
    split_props(normalize_props(props))
    send_view_patch
    return self
  end

  control = resolve_control(control_or_id)
  return self unless control
  wire_id = control.wire_id
  if wire_id.nil?
    # Events can race with navigation/disposal; never emit patch_control with nil id.
    refresh_control_indexes!
    wire_id = control.wire_id
  end
  return self if wire_id.nil?

  patch = normalize_props(props)
  if text_maps_to_content?(control, patch)
    patch["content"] = patch.delete("text")
  end

  patch.each { |key, value| control.props[key] = value }

  # Keep runtime control tree aligned with incremental patches.
  if patch.key?("controls")
    control.children.clear
    Array(patch["controls"]).each { |child| control.children << child if child.is_a?(Control) }
  end

  visited = Set.new
  patch.each_value { |value| register_embedded_value(value, visited) }

  patch_ops = patch.map { |k, v| [0, 0, k, serialize_patch_value(v)] }

  send_message(Protocol::ACTIONS[:patch_control], {
    "id" => wire_id,
    "patch" => [[0], *patch_ops]
  })

  self
end

#upload(files, timeout: nil, on_result: nil) ⇒ Object



855
856
857
858
859
860
861
862
# File 'lib/ruflet_ui/ruflet/page.rb', line 855

def upload(files, timeout: nil, on_result: nil)
  invoke_file_picker(
    "upload",
    { "files" => Array(files).map { |file| normalize_service_value(file) } },
    timeout: timeout,
    on_result: on_result
  )
end

#upload_files(files, timeout: nil, on_result: nil) ⇒ Object



864
865
866
# File 'lib/ruflet_ui/ruflet/page.rb', line 864

def upload_files(files, timeout: nil, on_result: nil)
  upload(files, timeout: timeout, on_result: on_result)
end

#url_launcher(**props) ⇒ Object



1007
1008
1009
# File 'lib/ruflet_ui/ruflet/page.rb', line 1007

def url_launcher(**props)
  service(:url_launcher, **props)
end

#user_accelerometer(**props) ⇒ Object



955
956
957
# File 'lib/ruflet_ui/ruflet/page.rb', line 955

def user_accelerometer(**props)
  service(:user_accelerometer, **props)
end

#vertical_alignmentObject



232
233
234
# File 'lib/ruflet_ui/ruflet/page.rb', line 232

def vertical_alignment
  @page_props["vertical_alignment"] || @view_props["vertical_alignment"]
end

#vertical_alignment=(value) ⇒ Object



236
237
238
239
240
# File 'lib/ruflet_ui/ruflet/page.rb', line 236

def vertical_alignment=(value)
  v = normalize_value("vertical_alignment", value)
  @page_props["vertical_alignment"] = v
  @view_props["vertical_alignment"] = v
end

#vibrate(timeout: 10, on_result: nil) ⇒ Object



884
885
886
# File 'lib/ruflet_ui/ruflet/page.rb', line 884

def vibrate(timeout: 10, on_result: nil)
  invoke_haptic_feedback("vibrate", timeout: timeout, on_result: on_result)
end

#wakelock(**props) ⇒ Object



340
341
342
343
344
# File 'lib/ruflet_ui/ruflet/page.rb', line 340

def wakelock(**props)
  return service(:wakelock, **props) unless props.empty?

  @wakelock_proxy
end

#webObject Also known as: web?

Older and embedded clients may omit this capability from registration. Ruflet applications should still be able to branch on page.web just as they do with a full desktop client.



21
22
23
# File 'lib/ruflet_ui/ruflet/page.rb', line 21

def web
  !!(@client_details && @client_details["web"])
end